diff options
Diffstat (limited to 'fs/fscache')
-rw-r--r-- | fs/fscache/cache.c | 32 | ||||
-rw-r--r-- | fs/fscache/cookie.c | 9 | ||||
-rw-r--r-- | fs/fscache/internal.h | 8 | ||||
-rw-r--r-- | fs/fscache/object-list.c | 10 | ||||
-rw-r--r-- | fs/fscache/object.c | 1018 | ||||
-rw-r--r-- | fs/fscache/operation.c | 22 | ||||
-rw-r--r-- | fs/fscache/page.c | 11 |
7 files changed, 571 insertions, 539 deletions
diff --git a/fs/fscache/cache.c b/fs/fscache/cache.c index 129ea537f023..f7cff367db7f 100644 --- a/fs/fscache/cache.c +++ b/fs/fscache/cache.c | |||
@@ -224,8 +224,10 @@ int fscache_add_cache(struct fscache_cache *cache, | |||
224 | BUG_ON(!ifsdef); | 224 | BUG_ON(!ifsdef); |
225 | 225 | ||
226 | cache->flags = 0; | 226 | cache->flags = 0; |
227 | ifsdef->event_mask = ULONG_MAX & ~(1 << FSCACHE_OBJECT_EV_CLEARED); | 227 | ifsdef->event_mask = |
228 | ifsdef->state = FSCACHE_OBJECT_ACTIVE; | 228 | ((1 << NR_FSCACHE_OBJECT_EVENTS) - 1) & |
229 | ~(1 << FSCACHE_OBJECT_EV_CLEARED); | ||
230 | __set_bit(FSCACHE_OBJECT_IS_AVAILABLE, &ifsdef->flags); | ||
229 | 231 | ||
230 | if (!tagname) | 232 | if (!tagname) |
231 | tagname = cache->identifier; | 233 | tagname = cache->identifier; |
@@ -330,25 +332,25 @@ static void fscache_withdraw_all_objects(struct fscache_cache *cache, | |||
330 | { | 332 | { |
331 | struct fscache_object *object; | 333 | struct fscache_object *object; |
332 | 334 | ||
333 | spin_lock(&cache->object_list_lock); | ||
334 | |||
335 | while (!list_empty(&cache->object_list)) { | 335 | while (!list_empty(&cache->object_list)) { |
336 | object = list_entry(cache->object_list.next, | 336 | spin_lock(&cache->object_list_lock); |
337 | struct fscache_object, cache_link); | ||
338 | list_move_tail(&object->cache_link, dying_objects); | ||
339 | 337 | ||
340 | _debug("withdraw %p", object->cookie); | 338 | if (!list_empty(&cache->object_list)) { |
339 | object = list_entry(cache->object_list.next, | ||
340 | struct fscache_object, cache_link); | ||
341 | list_move_tail(&object->cache_link, dying_objects); | ||
341 | 342 | ||
342 | spin_lock(&object->lock); | 343 | _debug("withdraw %p", object->cookie); |
343 | spin_unlock(&cache->object_list_lock); | 344 | |
344 | fscache_raise_event(object, FSCACHE_OBJECT_EV_WITHDRAW); | 345 | /* This must be done under object_list_lock to prevent |
345 | spin_unlock(&object->lock); | 346 | * a race with fscache_drop_object(). |
347 | */ | ||
348 | fscache_raise_event(object, FSCACHE_OBJECT_EV_KILL); | ||
349 | } | ||
346 | 350 | ||
351 | spin_unlock(&cache->object_list_lock); | ||
347 | cond_resched(); | 352 | cond_resched(); |
348 | spin_lock(&cache->object_list_lock); | ||
349 | } | 353 | } |
350 | |||
351 | spin_unlock(&cache->object_list_lock); | ||
352 | } | 354 | } |
353 | 355 | ||
354 | /** | 356 | /** |
diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c index a5f36c921e91..eee436646989 100644 --- a/fs/fscache/cookie.c +++ b/fs/fscache/cookie.c | |||
@@ -205,7 +205,7 @@ static int fscache_acquire_non_index_cookie(struct fscache_cookie *cookie) | |||
205 | 205 | ||
206 | /* initiate the process of looking up all the objects in the chain | 206 | /* initiate the process of looking up all the objects in the chain |
207 | * (done by fscache_initialise_object()) */ | 207 | * (done by fscache_initialise_object()) */ |
208 | fscache_enqueue_object(object); | 208 | fscache_raise_event(object, FSCACHE_OBJECT_EV_NEW_CHILD); |
209 | 209 | ||
210 | spin_unlock(&cookie->lock); | 210 | spin_unlock(&cookie->lock); |
211 | 211 | ||
@@ -469,7 +469,6 @@ void __fscache_relinquish_cookie(struct fscache_cookie *cookie, int retire) | |||
469 | { | 469 | { |
470 | struct fscache_cache *cache; | 470 | struct fscache_cache *cache; |
471 | struct fscache_object *object; | 471 | struct fscache_object *object; |
472 | unsigned long event; | ||
473 | 472 | ||
474 | fscache_stat(&fscache_n_relinquishes); | 473 | fscache_stat(&fscache_n_relinquishes); |
475 | if (retire) | 474 | if (retire) |
@@ -497,8 +496,6 @@ void __fscache_relinquish_cookie(struct fscache_cookie *cookie, int retire) | |||
497 | fscache_wait_bit, TASK_UNINTERRUPTIBLE); | 496 | fscache_wait_bit, TASK_UNINTERRUPTIBLE); |
498 | } | 497 | } |
499 | 498 | ||
500 | event = retire ? FSCACHE_OBJECT_EV_RETIRE : FSCACHE_OBJECT_EV_RELEASE; | ||
501 | |||
502 | try_again: | 499 | try_again: |
503 | spin_lock(&cookie->lock); | 500 | spin_lock(&cookie->lock); |
504 | 501 | ||
@@ -533,7 +530,9 @@ try_again: | |||
533 | 530 | ||
534 | cache = object->cache; | 531 | cache = object->cache; |
535 | object->cookie = NULL; | 532 | object->cookie = NULL; |
536 | fscache_raise_event(object, event); | 533 | if (retire) |
534 | set_bit(FSCACHE_OBJECT_RETIRE, &object->flags); | ||
535 | fscache_raise_event(object, FSCACHE_OBJECT_EV_KILL); | ||
537 | spin_unlock(&object->lock); | 536 | spin_unlock(&object->lock); |
538 | 537 | ||
539 | if (atomic_dec_and_test(&cookie->usage)) | 538 | if (atomic_dec_and_test(&cookie->usage)) |
diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index ee38fef4be51..3322d3c42ba8 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h | |||
@@ -97,10 +97,6 @@ extern int fscache_wait_bit_interruptible(void *); | |||
97 | /* | 97 | /* |
98 | * object.c | 98 | * object.c |
99 | */ | 99 | */ |
100 | extern const char fscache_object_states_short[FSCACHE_OBJECT__NSTATES][5]; | ||
101 | |||
102 | extern void fscache_withdrawing_object(struct fscache_cache *, | ||
103 | struct fscache_object *); | ||
104 | extern void fscache_enqueue_object(struct fscache_object *); | 100 | extern void fscache_enqueue_object(struct fscache_object *); |
105 | 101 | ||
106 | /* | 102 | /* |
@@ -291,6 +287,10 @@ static inline void fscache_raise_event(struct fscache_object *object, | |||
291 | unsigned event) | 287 | unsigned event) |
292 | { | 288 | { |
293 | BUG_ON(event >= NR_FSCACHE_OBJECT_EVENTS); | 289 | BUG_ON(event >= NR_FSCACHE_OBJECT_EVENTS); |
290 | #if 0 | ||
291 | printk("*** fscache_raise_event(OBJ%d{%lx},%x)\n", | ||
292 | object->debug_id, object->event_mask, (1 << event)); | ||
293 | #endif | ||
294 | if (!test_and_set_bit(event, &object->events) && | 294 | if (!test_and_set_bit(event, &object->events) && |
295 | test_bit(event, &object->event_mask)) | 295 | test_bit(event, &object->event_mask)) |
296 | fscache_enqueue_object(object); | 296 | fscache_enqueue_object(object); |
diff --git a/fs/fscache/object-list.c b/fs/fscache/object-list.c index f27c89d17885..4a386b080e03 100644 --- a/fs/fscache/object-list.c +++ b/fs/fscache/object-list.c | |||
@@ -174,7 +174,7 @@ static int fscache_objlist_show(struct seq_file *m, void *v) | |||
174 | 174 | ||
175 | if ((unsigned long) v == 1) { | 175 | if ((unsigned long) v == 1) { |
176 | seq_puts(m, "OBJECT PARENT STAT CHLDN OPS OOP IPR EX READS" | 176 | seq_puts(m, "OBJECT PARENT STAT CHLDN OPS OOP IPR EX READS" |
177 | " EM EV F S" | 177 | " EM EV FL S" |
178 | " | NETFS_COOKIE_DEF TY FL NETFS_DATA"); | 178 | " | NETFS_COOKIE_DEF TY FL NETFS_DATA"); |
179 | if (config & (FSCACHE_OBJLIST_CONFIG_KEY | | 179 | if (config & (FSCACHE_OBJLIST_CONFIG_KEY | |
180 | FSCACHE_OBJLIST_CONFIG_AUX)) | 180 | FSCACHE_OBJLIST_CONFIG_AUX)) |
@@ -193,7 +193,7 @@ static int fscache_objlist_show(struct seq_file *m, void *v) | |||
193 | 193 | ||
194 | if ((unsigned long) v == 2) { | 194 | if ((unsigned long) v == 2) { |
195 | seq_puts(m, "======== ======== ==== ===== === === === == =====" | 195 | seq_puts(m, "======== ======== ==== ===== === === === == =====" |
196 | " == == = =" | 196 | " == == == =" |
197 | " | ================ == == ================"); | 197 | " | ================ == == ================"); |
198 | if (config & (FSCACHE_OBJLIST_CONFIG_KEY | | 198 | if (config & (FSCACHE_OBJLIST_CONFIG_KEY | |
199 | FSCACHE_OBJLIST_CONFIG_AUX)) | 199 | FSCACHE_OBJLIST_CONFIG_AUX)) |
@@ -219,7 +219,7 @@ static int fscache_objlist_show(struct seq_file *m, void *v) | |||
219 | if (~config) { | 219 | if (~config) { |
220 | FILTER(obj->cookie, | 220 | FILTER(obj->cookie, |
221 | COOKIE, NOCOOKIE); | 221 | COOKIE, NOCOOKIE); |
222 | FILTER(obj->state != FSCACHE_OBJECT_ACTIVE || | 222 | FILTER(fscache_object_is_active(obj) || |
223 | obj->n_ops != 0 || | 223 | obj->n_ops != 0 || |
224 | obj->n_obj_ops != 0 || | 224 | obj->n_obj_ops != 0 || |
225 | obj->flags || | 225 | obj->flags || |
@@ -235,10 +235,10 @@ static int fscache_objlist_show(struct seq_file *m, void *v) | |||
235 | } | 235 | } |
236 | 236 | ||
237 | seq_printf(m, | 237 | seq_printf(m, |
238 | "%8x %8x %s %5u %3u %3u %3u %2u %5u %2lx %2lx %1lx %1x | ", | 238 | "%8x %8x %s %5u %3u %3u %3u %2u %5u %2lx %2lx %2lx %1x | ", |
239 | obj->debug_id, | 239 | obj->debug_id, |
240 | obj->parent ? obj->parent->debug_id : -1, | 240 | obj->parent ? obj->parent->debug_id : -1, |
241 | fscache_object_states_short[obj->state], | 241 | obj->state->short_name, |
242 | obj->n_children, | 242 | obj->n_children, |
243 | obj->n_ops, | 243 | obj->n_ops, |
244 | obj->n_obj_ops, | 244 | obj->n_obj_ops, |
diff --git a/fs/fscache/object.c b/fs/fscache/object.c index 863f6873c0f0..8f17debd7979 100644 --- a/fs/fscache/object.c +++ b/fs/fscache/object.c | |||
@@ -15,52 +15,133 @@ | |||
15 | #define FSCACHE_DEBUG_LEVEL COOKIE | 15 | #define FSCACHE_DEBUG_LEVEL COOKIE |
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | #include <linux/prefetch.h> | ||
18 | #include "internal.h" | 19 | #include "internal.h" |
19 | 20 | ||
20 | const char *fscache_object_states[FSCACHE_OBJECT__NSTATES] = { | 21 | static const struct fscache_state *fscache_abort_initialisation(struct fscache_object *, int); |
21 | [FSCACHE_OBJECT_INIT] = "OBJECT_INIT", | 22 | static const struct fscache_state *fscache_kill_dependents(struct fscache_object *, int); |
22 | [FSCACHE_OBJECT_LOOKING_UP] = "OBJECT_LOOKING_UP", | 23 | static const struct fscache_state *fscache_drop_object(struct fscache_object *, int); |
23 | [FSCACHE_OBJECT_CREATING] = "OBJECT_CREATING", | 24 | static const struct fscache_state *fscache_initialise_object(struct fscache_object *, int); |
24 | [FSCACHE_OBJECT_AVAILABLE] = "OBJECT_AVAILABLE", | 25 | static const struct fscache_state *fscache_invalidate_object(struct fscache_object *, int); |
25 | [FSCACHE_OBJECT_ACTIVE] = "OBJECT_ACTIVE", | 26 | static const struct fscache_state *fscache_jumpstart_dependents(struct fscache_object *, int); |
26 | [FSCACHE_OBJECT_INVALIDATING] = "OBJECT_INVALIDATING", | 27 | static const struct fscache_state *fscache_kill_object(struct fscache_object *, int); |
27 | [FSCACHE_OBJECT_UPDATING] = "OBJECT_UPDATING", | 28 | static const struct fscache_state *fscache_lookup_failure(struct fscache_object *, int); |
28 | [FSCACHE_OBJECT_DYING] = "OBJECT_DYING", | 29 | static const struct fscache_state *fscache_look_up_object(struct fscache_object *, int); |
29 | [FSCACHE_OBJECT_LC_DYING] = "OBJECT_LC_DYING", | 30 | static const struct fscache_state *fscache_object_available(struct fscache_object *, int); |
30 | [FSCACHE_OBJECT_ABORT_INIT] = "OBJECT_ABORT_INIT", | 31 | static const struct fscache_state *fscache_parent_ready(struct fscache_object *, int); |
31 | [FSCACHE_OBJECT_RELEASING] = "OBJECT_RELEASING", | 32 | static const struct fscache_state *fscache_update_object(struct fscache_object *, int); |
32 | [FSCACHE_OBJECT_RECYCLING] = "OBJECT_RECYCLING", | 33 | static const struct fscache_state *fscache_detach_from_cookie(struct fscache_object *, int); |
33 | [FSCACHE_OBJECT_WITHDRAWING] = "OBJECT_WITHDRAWING", | 34 | |
34 | [FSCACHE_OBJECT_DEAD] = "OBJECT_DEAD", | 35 | #define __STATE_NAME(n) fscache_osm_##n |
36 | #define STATE(n) (&__STATE_NAME(n)) | ||
37 | |||
38 | /* | ||
39 | * Define a work state. Work states are execution states. No event processing | ||
40 | * is performed by them. The function attached to a work state returns a | ||
41 | * pointer indicating the next state to which the state machine should | ||
42 | * transition. Returning NO_TRANSIT repeats the current state, but goes back | ||
43 | * to the scheduler first. | ||
44 | */ | ||
45 | #define WORK_STATE(n, sn, f) \ | ||
46 | const struct fscache_state __STATE_NAME(n) = { \ | ||
47 | .name = #n, \ | ||
48 | .short_name = sn, \ | ||
49 | .work = f \ | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * Returns from work states. | ||
54 | */ | ||
55 | #define transit_to(state) ({ prefetch(&STATE(state)->work); STATE(state); }) | ||
56 | |||
57 | #define NO_TRANSIT ((struct fscache_state *)NULL) | ||
58 | |||
59 | /* | ||
60 | * Define a wait state. Wait states are event processing states. No execution | ||
61 | * is performed by them. Wait states are just tables of "if event X occurs, | ||
62 | * clear it and transition to state Y". The dispatcher returns to the | ||
63 | * scheduler if none of the events in which the wait state has an interest are | ||
64 | * currently pending. | ||
65 | */ | ||
66 | #define WAIT_STATE(n, sn, ...) \ | ||
67 | const struct fscache_state __STATE_NAME(n) = { \ | ||
68 | .name = #n, \ | ||
69 | .short_name = sn, \ | ||
70 | .work = NULL, \ | ||
71 | .transitions = { __VA_ARGS__, { 0, NULL } } \ | ||
72 | } | ||
73 | |||
74 | #define TRANSIT_TO(state, emask) \ | ||
75 | { .events = (emask), .transit_to = STATE(state) } | ||
76 | |||
77 | /* | ||
78 | * The object state machine. | ||
79 | */ | ||
80 | static WORK_STATE(INIT_OBJECT, "INIT", fscache_initialise_object); | ||
81 | static WORK_STATE(PARENT_READY, "PRDY", fscache_parent_ready); | ||
82 | static WORK_STATE(ABORT_INIT, "ABRT", fscache_abort_initialisation); | ||
83 | static WORK_STATE(LOOK_UP_OBJECT, "LOOK", fscache_look_up_object); | ||
84 | static WORK_STATE(CREATE_OBJECT, "CRTO", fscache_look_up_object); | ||
85 | static WORK_STATE(OBJECT_AVAILABLE, "AVBL", fscache_object_available); | ||
86 | static WORK_STATE(JUMPSTART_DEPS, "JUMP", fscache_jumpstart_dependents); | ||
87 | |||
88 | static WORK_STATE(INVALIDATE_OBJECT, "INVL", fscache_invalidate_object); | ||
89 | static WORK_STATE(UPDATE_OBJECT, "UPDT", fscache_update_object); | ||
90 | |||
91 | static WORK_STATE(LOOKUP_FAILURE, "LCFL", fscache_lookup_failure); | ||
92 | static WORK_STATE(KILL_OBJECT, "KILL", fscache_kill_object); | ||
93 | static WORK_STATE(KILL_DEPENDENTS, "KDEP", fscache_kill_dependents); | ||
94 | static WORK_STATE(DROP_OBJECT, "DROP", fscache_drop_object); | ||
95 | static WORK_STATE(DETACH_FROM_COOKIE, "DTCH", fscache_detach_from_cookie); | ||
96 | static WORK_STATE(OBJECT_DEAD, "DEAD", (void*)2UL); | ||
97 | |||
98 | static WAIT_STATE(WAIT_FOR_INIT, "?INI", | ||
99 | TRANSIT_TO(INIT_OBJECT, 1 << FSCACHE_OBJECT_EV_NEW_CHILD)); | ||
100 | |||
101 | static WAIT_STATE(WAIT_FOR_PARENT, "?PRN", | ||
102 | TRANSIT_TO(PARENT_READY, 1 << FSCACHE_OBJECT_EV_PARENT_READY)); | ||
103 | |||
104 | static WAIT_STATE(WAIT_FOR_CMD, "?CMD", | ||
105 | TRANSIT_TO(INVALIDATE_OBJECT, 1 << FSCACHE_OBJECT_EV_INVALIDATE), | ||
106 | TRANSIT_TO(UPDATE_OBJECT, 1 << FSCACHE_OBJECT_EV_UPDATE), | ||
107 | TRANSIT_TO(JUMPSTART_DEPS, 1 << FSCACHE_OBJECT_EV_NEW_CHILD)); | ||
108 | |||
109 | static WAIT_STATE(WAIT_FOR_CLEARANCE, "?CLR", | ||
110 | TRANSIT_TO(KILL_OBJECT, 1 << FSCACHE_OBJECT_EV_CLEARED)); | ||
111 | |||
112 | /* | ||
113 | * Out-of-band event transition tables. These are for handling unexpected | ||
114 | * events, such as an I/O error. If an OOB event occurs, the state machine | ||
115 | * clears and disables the event and forces a transition to the nominated work | ||
116 | * state (acurrently executing work states will complete first). | ||
117 | * | ||
118 | * In such a situation, object->state remembers the state the machine should | ||
119 | * have been in/gone to and returning NO_TRANSIT returns to that. | ||
120 | */ | ||
121 | static const struct fscache_transition fscache_osm_init_oob[] = { | ||
122 | TRANSIT_TO(ABORT_INIT, | ||
123 | (1 << FSCACHE_OBJECT_EV_ERROR) | | ||
124 | (1 << FSCACHE_OBJECT_EV_KILL)), | ||
125 | { 0, NULL } | ||
35 | }; | 126 | }; |
36 | EXPORT_SYMBOL(fscache_object_states); | 127 | |
37 | 128 | static const struct fscache_transition fscache_osm_lookup_oob[] = { | |
38 | const char fscache_object_states_short[FSCACHE_OBJECT__NSTATES][5] = { | 129 | TRANSIT_TO(LOOKUP_FAILURE, |
39 | [FSCACHE_OBJECT_INIT] = "INIT", | 130 | (1 << FSCACHE_OBJECT_EV_ERROR) | |
40 | [FSCACHE_OBJECT_LOOKING_UP] = "LOOK", | 131 | (1 << FSCACHE_OBJECT_EV_KILL)), |
41 | [FSCACHE_OBJECT_CREATING] = "CRTN", | 132 | { 0, NULL } |
42 | [FSCACHE_OBJECT_AVAILABLE] = "AVBL", | 133 | }; |
43 | [FSCACHE_OBJECT_ACTIVE] = "ACTV", | 134 | |
44 | [FSCACHE_OBJECT_INVALIDATING] = "INVL", | 135 | static const struct fscache_transition fscache_osm_run_oob[] = { |
45 | [FSCACHE_OBJECT_UPDATING] = "UPDT", | 136 | TRANSIT_TO(KILL_OBJECT, |
46 | [FSCACHE_OBJECT_DYING] = "DYNG", | 137 | (1 << FSCACHE_OBJECT_EV_ERROR) | |
47 | [FSCACHE_OBJECT_LC_DYING] = "LCDY", | 138 | (1 << FSCACHE_OBJECT_EV_KILL)), |
48 | [FSCACHE_OBJECT_ABORT_INIT] = "ABTI", | 139 | { 0, NULL } |
49 | [FSCACHE_OBJECT_RELEASING] = "RELS", | ||
50 | [FSCACHE_OBJECT_RECYCLING] = "RCYC", | ||
51 | [FSCACHE_OBJECT_WITHDRAWING] = "WTHD", | ||
52 | [FSCACHE_OBJECT_DEAD] = "DEAD", | ||
53 | }; | 140 | }; |
54 | 141 | ||
55 | static int fscache_get_object(struct fscache_object *); | 142 | static int fscache_get_object(struct fscache_object *); |
56 | static void fscache_put_object(struct fscache_object *); | 143 | static void fscache_put_object(struct fscache_object *); |
57 | static void fscache_initialise_object(struct fscache_object *); | 144 | static bool fscache_enqueue_dependents(struct fscache_object *, int); |
58 | static void fscache_lookup_object(struct fscache_object *); | ||
59 | static void fscache_object_available(struct fscache_object *); | ||
60 | static void fscache_invalidate_object(struct fscache_object *); | ||
61 | static void fscache_release_object(struct fscache_object *); | ||
62 | static void fscache_withdraw_object(struct fscache_object *); | ||
63 | static void fscache_enqueue_dependents(struct fscache_object *); | ||
64 | static void fscache_dequeue_object(struct fscache_object *); | 145 | static void fscache_dequeue_object(struct fscache_object *); |
65 | 146 | ||
66 | /* | 147 | /* |
@@ -83,281 +164,102 @@ static inline void fscache_done_parent_op(struct fscache_object *object) | |||
83 | } | 164 | } |
84 | 165 | ||
85 | /* | 166 | /* |
86 | * Notify netfs of invalidation completion. | 167 | * Object state machine dispatcher. |
87 | */ | 168 | */ |
88 | static inline void fscache_invalidation_complete(struct fscache_cookie *cookie) | 169 | static void fscache_object_sm_dispatcher(struct fscache_object *object) |
89 | { | 170 | { |
90 | if (test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) | 171 | const struct fscache_transition *t; |
91 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); | 172 | const struct fscache_state *state, *new_state; |
92 | } | 173 | unsigned long events, event_mask; |
93 | 174 | int event = -1; | |
94 | /* | ||
95 | * process events that have been sent to an object's state machine | ||
96 | * - initiates parent lookup | ||
97 | * - does object lookup | ||
98 | * - does object creation | ||
99 | * - does object recycling and retirement | ||
100 | * - does object withdrawal | ||
101 | */ | ||
102 | static void fscache_object_state_machine(struct fscache_object *object) | ||
103 | { | ||
104 | enum fscache_object_state new_state; | ||
105 | struct fscache_cookie *cookie; | ||
106 | int event; | ||
107 | 175 | ||
108 | ASSERT(object != NULL); | 176 | ASSERT(object != NULL); |
109 | 177 | ||
110 | _enter("{OBJ%x,%s,%lx}", | 178 | _enter("{OBJ%x,%s,%lx}", |
111 | object->debug_id, fscache_object_states[object->state], | 179 | object->debug_id, object->state->name, object->events); |
112 | object->events); | 180 | |
113 | 181 | event_mask = object->event_mask; | |
114 | switch (object->state) { | 182 | restart: |
115 | /* wait for the parent object to become ready */ | 183 | object->event_mask = 0; /* Mask normal event handling */ |
116 | case FSCACHE_OBJECT_INIT: | 184 | state = object->state; |
117 | object->event_mask = | 185 | restart_masked: |
118 | FSCACHE_OBJECT_EVENTS_MASK & | 186 | events = object->events; |
119 | ~(1 << FSCACHE_OBJECT_EV_CLEARED); | 187 | |
120 | fscache_initialise_object(object); | 188 | /* Handle any out-of-band events (typically an error) */ |
121 | goto done; | 189 | if (events & object->oob_event_mask) { |
122 | 190 | _debug("{OBJ%x} oob %lx", | |
123 | /* look up the object metadata on disk */ | 191 | object->debug_id, events & object->oob_event_mask); |
124 | case FSCACHE_OBJECT_LOOKING_UP: | 192 | for (t = object->oob_table; t->events; t++) { |
125 | fscache_lookup_object(object); | 193 | if (events & t->events) { |
126 | goto lookup_transit; | 194 | state = t->transit_to; |
127 | 195 | ASSERT(state->work != NULL); | |
128 | /* create the object metadata on disk */ | 196 | event = fls(events & t->events) - 1; |
129 | case FSCACHE_OBJECT_CREATING: | 197 | __clear_bit(event, &object->oob_event_mask); |
130 | fscache_lookup_object(object); | 198 | clear_bit(event, &object->events); |
131 | goto lookup_transit; | 199 | goto execute_work_state; |
132 | 200 | } | |
133 | /* handle an object becoming available; start pending | ||
134 | * operations and queue dependent operations for processing */ | ||
135 | case FSCACHE_OBJECT_AVAILABLE: | ||
136 | fscache_object_available(object); | ||
137 | goto active_transit; | ||
138 | |||
139 | /* normal running state */ | ||
140 | case FSCACHE_OBJECT_ACTIVE: | ||
141 | goto active_transit; | ||
142 | |||
143 | /* Invalidate an object on disk */ | ||
144 | case FSCACHE_OBJECT_INVALIDATING: | ||
145 | clear_bit(FSCACHE_OBJECT_EV_INVALIDATE, &object->events); | ||
146 | fscache_stat(&fscache_n_invalidates_run); | ||
147 | fscache_stat(&fscache_n_cop_invalidate_object); | ||
148 | fscache_invalidate_object(object); | ||
149 | fscache_stat_d(&fscache_n_cop_invalidate_object); | ||
150 | fscache_raise_event(object, FSCACHE_OBJECT_EV_UPDATE); | ||
151 | goto active_transit; | ||
152 | |||
153 | /* update the object metadata on disk */ | ||
154 | case FSCACHE_OBJECT_UPDATING: | ||
155 | clear_bit(FSCACHE_OBJECT_EV_UPDATE, &object->events); | ||
156 | fscache_stat(&fscache_n_updates_run); | ||
157 | fscache_stat(&fscache_n_cop_update_object); | ||
158 | object->cache->ops->update_object(object); | ||
159 | fscache_stat_d(&fscache_n_cop_update_object); | ||
160 | goto active_transit; | ||
161 | |||
162 | /* handle an object dying during lookup or creation */ | ||
163 | case FSCACHE_OBJECT_LC_DYING: | ||
164 | object->event_mask &= ~(1 << FSCACHE_OBJECT_EV_UPDATE); | ||
165 | fscache_stat(&fscache_n_cop_lookup_complete); | ||
166 | object->cache->ops->lookup_complete(object); | ||
167 | fscache_stat_d(&fscache_n_cop_lookup_complete); | ||
168 | |||
169 | spin_lock(&object->lock); | ||
170 | object->state = FSCACHE_OBJECT_DYING; | ||
171 | cookie = object->cookie; | ||
172 | if (cookie) { | ||
173 | if (test_and_clear_bit(FSCACHE_COOKIE_LOOKING_UP, | ||
174 | &cookie->flags)) | ||
175 | wake_up_bit(&cookie->flags, | ||
176 | FSCACHE_COOKIE_LOOKING_UP); | ||
177 | if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, | ||
178 | &cookie->flags)) | ||
179 | wake_up_bit(&cookie->flags, | ||
180 | FSCACHE_COOKIE_CREATING); | ||
181 | } | 201 | } |
182 | spin_unlock(&object->lock); | 202 | } |
183 | 203 | ||
184 | fscache_done_parent_op(object); | 204 | /* Wait states are just transition tables */ |
205 | if (!state->work) { | ||
206 | if (events & event_mask) { | ||
207 | for (t = state->transitions; t->events; t++) { | ||
208 | if (events & t->events) { | ||
209 | new_state = t->transit_to; | ||
210 | event = fls(events & t->events) - 1; | ||
211 | clear_bit(event, &object->events); | ||
212 | _debug("{OBJ%x} ev %d: %s -> %s", | ||
213 | object->debug_id, event, | ||
214 | state->name, new_state->name); | ||
215 | object->state = state = new_state; | ||
216 | goto execute_work_state; | ||
217 | } | ||
218 | } | ||
185 | 219 | ||
186 | /* wait for completion of all active operations on this object | 220 | /* The event mask didn't include all the tabled bits */ |
187 | * and the death of all child objects of this object */ | 221 | BUG(); |
188 | case FSCACHE_OBJECT_DYING: | ||
189 | dying: | ||
190 | clear_bit(FSCACHE_OBJECT_EV_CLEARED, &object->events); | ||
191 | spin_lock(&object->lock); | ||
192 | _debug("dying OBJ%x {%d,%d}", | ||
193 | object->debug_id, object->n_ops, object->n_children); | ||
194 | if (object->n_ops == 0 && object->n_children == 0) { | ||
195 | object->event_mask &= | ||
196 | ~(1 << FSCACHE_OBJECT_EV_CLEARED); | ||
197 | object->event_mask |= | ||
198 | (1 << FSCACHE_OBJECT_EV_WITHDRAW) | | ||
199 | (1 << FSCACHE_OBJECT_EV_RETIRE) | | ||
200 | (1 << FSCACHE_OBJECT_EV_RELEASE) | | ||
201 | (1 << FSCACHE_OBJECT_EV_ERROR); | ||
202 | } else { | ||
203 | object->event_mask &= | ||
204 | ~((1 << FSCACHE_OBJECT_EV_WITHDRAW) | | ||
205 | (1 << FSCACHE_OBJECT_EV_RETIRE) | | ||
206 | (1 << FSCACHE_OBJECT_EV_RELEASE) | | ||
207 | (1 << FSCACHE_OBJECT_EV_ERROR)); | ||
208 | object->event_mask |= | ||
209 | 1 << FSCACHE_OBJECT_EV_CLEARED; | ||
210 | } | 222 | } |
211 | spin_unlock(&object->lock); | 223 | /* Randomly woke up */ |
212 | fscache_enqueue_dependents(object); | 224 | goto unmask_events; |
213 | fscache_start_operations(object); | ||
214 | goto terminal_transit; | ||
215 | |||
216 | /* handle an abort during initialisation */ | ||
217 | case FSCACHE_OBJECT_ABORT_INIT: | ||
218 | _debug("handle abort init %lx", object->events); | ||
219 | object->event_mask &= ~(1 << FSCACHE_OBJECT_EV_UPDATE); | ||
220 | |||
221 | spin_lock(&object->lock); | ||
222 | fscache_dequeue_object(object); | ||
223 | |||
224 | object->state = FSCACHE_OBJECT_DYING; | ||
225 | if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, | ||
226 | &object->cookie->flags)) | ||
227 | wake_up_bit(&object->cookie->flags, | ||
228 | FSCACHE_COOKIE_CREATING); | ||
229 | spin_unlock(&object->lock); | ||
230 | goto dying; | ||
231 | |||
232 | /* handle the netfs releasing an object and possibly marking it | ||
233 | * obsolete too */ | ||
234 | case FSCACHE_OBJECT_RELEASING: | ||
235 | case FSCACHE_OBJECT_RECYCLING: | ||
236 | object->event_mask &= | ||
237 | ~((1 << FSCACHE_OBJECT_EV_WITHDRAW) | | ||
238 | (1 << FSCACHE_OBJECT_EV_RETIRE) | | ||
239 | (1 << FSCACHE_OBJECT_EV_RELEASE) | | ||
240 | (1 << FSCACHE_OBJECT_EV_ERROR)); | ||
241 | fscache_release_object(object); | ||
242 | spin_lock(&object->lock); | ||
243 | object->state = FSCACHE_OBJECT_DEAD; | ||
244 | spin_unlock(&object->lock); | ||
245 | fscache_stat(&fscache_n_object_dead); | ||
246 | goto terminal_transit; | ||
247 | |||
248 | /* handle the parent cache of this object being withdrawn from | ||
249 | * active service */ | ||
250 | case FSCACHE_OBJECT_WITHDRAWING: | ||
251 | object->event_mask &= | ||
252 | ~((1 << FSCACHE_OBJECT_EV_WITHDRAW) | | ||
253 | (1 << FSCACHE_OBJECT_EV_RETIRE) | | ||
254 | (1 << FSCACHE_OBJECT_EV_RELEASE) | | ||
255 | (1 << FSCACHE_OBJECT_EV_ERROR)); | ||
256 | fscache_withdraw_object(object); | ||
257 | spin_lock(&object->lock); | ||
258 | object->state = FSCACHE_OBJECT_DEAD; | ||
259 | spin_unlock(&object->lock); | ||
260 | fscache_stat(&fscache_n_object_dead); | ||
261 | goto terminal_transit; | ||
262 | |||
263 | /* complain about the object being woken up once it is | ||
264 | * deceased */ | ||
265 | case FSCACHE_OBJECT_DEAD: | ||
266 | printk(KERN_ERR "FS-Cache:" | ||
267 | " Unexpected event in dead state %lx\n", | ||
268 | object->events & object->event_mask); | ||
269 | BUG(); | ||
270 | |||
271 | default: | ||
272 | printk(KERN_ERR "FS-Cache: Unknown object state %u\n", | ||
273 | object->state); | ||
274 | BUG(); | ||
275 | } | 225 | } |
276 | 226 | ||
277 | /* determine the transition from a lookup state */ | 227 | execute_work_state: |
278 | lookup_transit: | 228 | _debug("{OBJ%x} exec %s", object->debug_id, state->name); |
279 | event = fls(object->events & object->event_mask) - 1; | ||
280 | switch (event) { | ||
281 | case FSCACHE_OBJECT_EV_WITHDRAW: | ||
282 | case FSCACHE_OBJECT_EV_RETIRE: | ||
283 | case FSCACHE_OBJECT_EV_RELEASE: | ||
284 | case FSCACHE_OBJECT_EV_ERROR: | ||
285 | new_state = FSCACHE_OBJECT_LC_DYING; | ||
286 | goto change_state; | ||
287 | case FSCACHE_OBJECT_EV_INVALIDATE: | ||
288 | new_state = FSCACHE_OBJECT_INVALIDATING; | ||
289 | goto change_state; | ||
290 | case FSCACHE_OBJECT_EV_REQUEUE: | ||
291 | goto done; | ||
292 | case -1: | ||
293 | goto done; /* sleep until event */ | ||
294 | default: | ||
295 | goto unsupported_event; | ||
296 | } | ||
297 | |||
298 | /* determine the transition from an active state */ | ||
299 | active_transit: | ||
300 | event = fls(object->events & object->event_mask) - 1; | ||
301 | switch (event) { | ||
302 | case FSCACHE_OBJECT_EV_WITHDRAW: | ||
303 | case FSCACHE_OBJECT_EV_RETIRE: | ||
304 | case FSCACHE_OBJECT_EV_RELEASE: | ||
305 | case FSCACHE_OBJECT_EV_ERROR: | ||
306 | new_state = FSCACHE_OBJECT_DYING; | ||
307 | goto change_state; | ||
308 | case FSCACHE_OBJECT_EV_INVALIDATE: | ||
309 | new_state = FSCACHE_OBJECT_INVALIDATING; | ||
310 | goto change_state; | ||
311 | case FSCACHE_OBJECT_EV_UPDATE: | ||
312 | new_state = FSCACHE_OBJECT_UPDATING; | ||
313 | goto change_state; | ||
314 | case -1: | ||
315 | new_state = FSCACHE_OBJECT_ACTIVE; | ||
316 | goto change_state; /* sleep until event */ | ||
317 | default: | ||
318 | goto unsupported_event; | ||
319 | } | ||
320 | 229 | ||
321 | /* determine the transition from a terminal state */ | 230 | new_state = state->work(object, event); |
322 | terminal_transit: | 231 | event = -1; |
323 | event = fls(object->events & object->event_mask) - 1; | 232 | if (new_state == NO_TRANSIT) { |
324 | switch (event) { | 233 | _debug("{OBJ%x} %s notrans", object->debug_id, state->name); |
325 | case FSCACHE_OBJECT_EV_WITHDRAW: | 234 | fscache_enqueue_object(object); |
326 | new_state = FSCACHE_OBJECT_WITHDRAWING; | 235 | event_mask = object->oob_event_mask; |
327 | goto change_state; | 236 | goto unmask_events; |
328 | case FSCACHE_OBJECT_EV_RETIRE: | ||
329 | new_state = FSCACHE_OBJECT_RECYCLING; | ||
330 | goto change_state; | ||
331 | case FSCACHE_OBJECT_EV_RELEASE: | ||
332 | new_state = FSCACHE_OBJECT_RELEASING; | ||
333 | goto change_state; | ||
334 | case FSCACHE_OBJECT_EV_ERROR: | ||
335 | new_state = FSCACHE_OBJECT_WITHDRAWING; | ||
336 | goto change_state; | ||
337 | case FSCACHE_OBJECT_EV_CLEARED: | ||
338 | new_state = FSCACHE_OBJECT_DYING; | ||
339 | goto change_state; | ||
340 | case -1: | ||
341 | goto done; /* sleep until event */ | ||
342 | default: | ||
343 | goto unsupported_event; | ||
344 | } | 237 | } |
345 | 238 | ||
346 | change_state: | 239 | _debug("{OBJ%x} %s -> %s", |
347 | spin_lock(&object->lock); | 240 | object->debug_id, state->name, new_state->name); |
348 | object->state = new_state; | 241 | object->state = state = new_state; |
349 | spin_unlock(&object->lock); | ||
350 | 242 | ||
351 | done: | 243 | if (state->work) { |
352 | _leave(" [->%s]", fscache_object_states[object->state]); | 244 | if (unlikely(state->work == ((void *)2UL))) { |
353 | return; | 245 | _leave(" [dead]"); |
246 | return; | ||
247 | } | ||
248 | goto restart_masked; | ||
249 | } | ||
354 | 250 | ||
355 | unsupported_event: | 251 | /* Transited to wait state */ |
356 | printk(KERN_ERR "FS-Cache:" | 252 | event_mask = object->oob_event_mask; |
357 | " Unsupported event %d [%lx/%lx] in state %s\n", | 253 | for (t = state->transitions; t->events; t++) |
358 | event, object->events, object->event_mask, | 254 | event_mask |= t->events; |
359 | fscache_object_states[object->state]); | 255 | |
360 | BUG(); | 256 | unmask_events: |
257 | object->event_mask = event_mask; | ||
258 | smp_mb(); | ||
259 | events = object->events; | ||
260 | if (events & event_mask) | ||
261 | goto restart; | ||
262 | _leave(" [msk %lx]", event_mask); | ||
361 | } | 263 | } |
362 | 264 | ||
363 | /* | 265 | /* |
@@ -372,11 +274,8 @@ static void fscache_object_work_func(struct work_struct *work) | |||
372 | _enter("{OBJ%x}", object->debug_id); | 274 | _enter("{OBJ%x}", object->debug_id); |
373 | 275 | ||
374 | start = jiffies; | 276 | start = jiffies; |
375 | fscache_object_state_machine(object); | 277 | fscache_object_sm_dispatcher(object); |
376 | fscache_hist(fscache_objs_histogram, start); | 278 | fscache_hist(fscache_objs_histogram, start); |
377 | if (object->events & object->event_mask) | ||
378 | fscache_enqueue_object(object); | ||
379 | clear_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
380 | fscache_put_object(object); | 279 | fscache_put_object(object); |
381 | } | 280 | } |
382 | 281 | ||
@@ -395,9 +294,13 @@ void fscache_object_init(struct fscache_object *object, | |||
395 | struct fscache_cookie *cookie, | 294 | struct fscache_cookie *cookie, |
396 | struct fscache_cache *cache) | 295 | struct fscache_cache *cache) |
397 | { | 296 | { |
297 | const struct fscache_transition *t; | ||
298 | |||
398 | atomic_inc(&cache->object_count); | 299 | atomic_inc(&cache->object_count); |
399 | 300 | ||
400 | object->state = FSCACHE_OBJECT_INIT; | 301 | object->state = STATE(WAIT_FOR_INIT); |
302 | object->oob_table = fscache_osm_init_oob; | ||
303 | object->flags = 1 << FSCACHE_OBJECT_IS_LIVE; | ||
401 | spin_lock_init(&object->lock); | 304 | spin_lock_init(&object->lock); |
402 | INIT_LIST_HEAD(&object->cache_link); | 305 | INIT_LIST_HEAD(&object->cache_link); |
403 | INIT_HLIST_NODE(&object->cookie_link); | 306 | INIT_HLIST_NODE(&object->cookie_link); |
@@ -407,17 +310,48 @@ void fscache_object_init(struct fscache_object *object, | |||
407 | INIT_LIST_HEAD(&object->pending_ops); | 310 | INIT_LIST_HEAD(&object->pending_ops); |
408 | object->n_children = 0; | 311 | object->n_children = 0; |
409 | object->n_ops = object->n_in_progress = object->n_exclusive = 0; | 312 | object->n_ops = object->n_in_progress = object->n_exclusive = 0; |
410 | object->events = object->event_mask = 0; | 313 | object->events = 0; |
411 | object->flags = 0; | ||
412 | object->store_limit = 0; | 314 | object->store_limit = 0; |
413 | object->store_limit_l = 0; | 315 | object->store_limit_l = 0; |
414 | object->cache = cache; | 316 | object->cache = cache; |
415 | object->cookie = cookie; | 317 | object->cookie = cookie; |
416 | object->parent = NULL; | 318 | object->parent = NULL; |
319 | |||
320 | object->oob_event_mask = 0; | ||
321 | for (t = object->oob_table; t->events; t++) | ||
322 | object->oob_event_mask |= t->events; | ||
323 | object->event_mask = object->oob_event_mask; | ||
324 | for (t = object->state->transitions; t->events; t++) | ||
325 | object->event_mask |= t->events; | ||
417 | } | 326 | } |
418 | EXPORT_SYMBOL(fscache_object_init); | 327 | EXPORT_SYMBOL(fscache_object_init); |
419 | 328 | ||
420 | /* | 329 | /* |
330 | * Abort object initialisation before we start it. | ||
331 | */ | ||
332 | static const struct fscache_state *fscache_abort_initialisation(struct fscache_object *object, | ||
333 | int event) | ||
334 | { | ||
335 | struct fscache_cookie *cookie; | ||
336 | |||
337 | _enter("{OBJ%x},%d", object->debug_id, event); | ||
338 | |||
339 | object->oob_event_mask = 0; | ||
340 | clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); | ||
341 | |||
342 | fscache_dequeue_object(object); | ||
343 | |||
344 | spin_lock(&object->lock); | ||
345 | cookie = object->cookie; | ||
346 | clear_bit_unlock(FSCACHE_COOKIE_CREATING, &cookie->flags); | ||
347 | spin_unlock(&object->lock); | ||
348 | |||
349 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_CREATING); | ||
350 | |||
351 | return transit_to(KILL_OBJECT); | ||
352 | } | ||
353 | |||
354 | /* | ||
421 | * initialise an object | 355 | * initialise an object |
422 | * - check the specified object's parent to see if we can make use of it | 356 | * - check the specified object's parent to see if we can make use of it |
423 | * immediately to do a creation | 357 | * immediately to do a creation |
@@ -426,74 +360,78 @@ EXPORT_SYMBOL(fscache_object_init); | |||
426 | * - an object's cookie is pinned until we clear FSCACHE_COOKIE_CREATING on the | 360 | * - an object's cookie is pinned until we clear FSCACHE_COOKIE_CREATING on the |
427 | * leaf-most cookies of the object and all its children | 361 | * leaf-most cookies of the object and all its children |
428 | */ | 362 | */ |
429 | static void fscache_initialise_object(struct fscache_object *object) | 363 | static const struct fscache_state *fscache_initialise_object(struct fscache_object *object, |
364 | int event) | ||
430 | { | 365 | { |
431 | struct fscache_object *parent; | 366 | struct fscache_object *parent; |
367 | bool success; | ||
432 | 368 | ||
433 | _enter(""); | 369 | _enter("{OBJ%x},%d", object->debug_id, event); |
434 | ASSERT(object->cookie != NULL); | ||
435 | ASSERT(object->cookie->parent != NULL); | ||
436 | 370 | ||
437 | if (object->events & ((1 << FSCACHE_OBJECT_EV_ERROR) | | 371 | ASSERT(list_empty(&object->dep_link)); |
438 | (1 << FSCACHE_OBJECT_EV_RELEASE) | | ||
439 | (1 << FSCACHE_OBJECT_EV_RETIRE) | | ||
440 | (1 << FSCACHE_OBJECT_EV_WITHDRAW))) { | ||
441 | _debug("abort init %lx", object->events); | ||
442 | spin_lock(&object->lock); | ||
443 | object->state = FSCACHE_OBJECT_ABORT_INIT; | ||
444 | spin_unlock(&object->lock); | ||
445 | return; | ||
446 | } | ||
447 | |||
448 | spin_lock(&object->cookie->lock); | ||
449 | spin_lock_nested(&object->cookie->parent->lock, 1); | ||
450 | 372 | ||
451 | parent = object->parent; | 373 | parent = object->parent; |
452 | if (!parent) { | 374 | if (!parent) { |
453 | _debug("no parent"); | 375 | _leave(" [no parent]"); |
454 | set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); | 376 | return transit_to(DETACH_FROM_COOKIE); |
455 | } else { | 377 | } |
456 | spin_lock(&object->lock); | ||
457 | spin_lock_nested(&parent->lock, 1); | ||
458 | _debug("parent %s", fscache_object_states[parent->state]); | ||
459 | |||
460 | if (fscache_object_is_dying(parent)) { | ||
461 | _debug("bad parent"); | ||
462 | set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); | ||
463 | } else if (!fscache_object_is_available(parent)) { | ||
464 | _debug("wait"); | ||
465 | |||
466 | /* we may get woken up in this state by child objects | ||
467 | * binding on to us, so we need to make sure we don't | ||
468 | * add ourself to the list multiple times */ | ||
469 | if (list_empty(&object->dep_link)) { | ||
470 | fscache_stat(&fscache_n_cop_grab_object); | ||
471 | object->cache->ops->grab_object(object); | ||
472 | fscache_stat_d(&fscache_n_cop_grab_object); | ||
473 | list_add(&object->dep_link, | ||
474 | &parent->dependents); | ||
475 | |||
476 | /* fscache_acquire_non_index_cookie() uses this | ||
477 | * to wake the chain up */ | ||
478 | if (parent->state == FSCACHE_OBJECT_INIT) | ||
479 | fscache_enqueue_object(parent); | ||
480 | } | ||
481 | } else { | ||
482 | _debug("go"); | ||
483 | parent->n_ops++; | ||
484 | parent->n_obj_ops++; | ||
485 | object->lookup_jif = jiffies; | ||
486 | object->state = FSCACHE_OBJECT_LOOKING_UP; | ||
487 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
488 | } | ||
489 | 378 | ||
490 | spin_unlock(&parent->lock); | 379 | _debug("parent %s", parent->state->name); |
491 | spin_unlock(&object->lock); | 380 | |
381 | if (fscache_object_is_dying(parent)) { | ||
382 | _leave(" [bad parent]"); | ||
383 | return transit_to(DETACH_FROM_COOKIE); | ||
384 | } | ||
385 | |||
386 | if (fscache_object_is_available(parent)) { | ||
387 | _leave(" [ready]"); | ||
388 | return transit_to(PARENT_READY); | ||
389 | } | ||
390 | |||
391 | _debug("wait"); | ||
392 | |||
393 | spin_lock(&parent->lock); | ||
394 | fscache_stat(&fscache_n_cop_grab_object); | ||
395 | success = false; | ||
396 | if (fscache_object_is_live(parent) && | ||
397 | object->cache->ops->grab_object(object)) { | ||
398 | list_add(&object->dep_link, &parent->dependents); | ||
399 | success = true; | ||
400 | } | ||
401 | fscache_stat_d(&fscache_n_cop_grab_object); | ||
402 | spin_unlock(&parent->lock); | ||
403 | if (!success) { | ||
404 | _leave(" [grab failed]"); | ||
405 | return transit_to(DETACH_FROM_COOKIE); | ||
492 | } | 406 | } |
493 | 407 | ||
494 | spin_unlock(&object->cookie->parent->lock); | 408 | /* fscache_acquire_non_index_cookie() uses this |
495 | spin_unlock(&object->cookie->lock); | 409 | * to wake the chain up */ |
410 | fscache_raise_event(parent, FSCACHE_OBJECT_EV_NEW_CHILD); | ||
411 | _leave(" [wait]"); | ||
412 | return transit_to(WAIT_FOR_PARENT); | ||
413 | } | ||
414 | |||
415 | /* | ||
416 | * Once the parent object is ready, we should kick off our lookup op. | ||
417 | */ | ||
418 | static const struct fscache_state *fscache_parent_ready(struct fscache_object *object, | ||
419 | int event) | ||
420 | { | ||
421 | struct fscache_object *parent = object->parent; | ||
422 | |||
423 | _enter("{OBJ%x},%d", object->debug_id, event); | ||
424 | |||
425 | ASSERT(parent != NULL); | ||
426 | |||
427 | spin_lock(&parent->lock); | ||
428 | parent->n_ops++; | ||
429 | parent->n_obj_ops++; | ||
430 | object->lookup_jif = jiffies; | ||
431 | spin_unlock(&parent->lock); | ||
432 | |||
496 | _leave(""); | 433 | _leave(""); |
434 | return transit_to(LOOK_UP_OBJECT); | ||
497 | } | 435 | } |
498 | 436 | ||
499 | /* | 437 | /* |
@@ -503,15 +441,17 @@ static void fscache_initialise_object(struct fscache_object *object) | |||
503 | * - an object's cookie is pinned until we clear FSCACHE_COOKIE_CREATING on the | 441 | * - an object's cookie is pinned until we clear FSCACHE_COOKIE_CREATING on the |
504 | * leaf-most cookies of the object and all its children | 442 | * leaf-most cookies of the object and all its children |
505 | */ | 443 | */ |
506 | static void fscache_lookup_object(struct fscache_object *object) | 444 | static const struct fscache_state *fscache_look_up_object(struct fscache_object *object, |
445 | int event) | ||
507 | { | 446 | { |
508 | struct fscache_cookie *cookie = object->cookie; | 447 | struct fscache_cookie *cookie = object->cookie; |
509 | struct fscache_object *parent; | 448 | struct fscache_object *parent = object->parent; |
510 | int ret; | 449 | int ret; |
511 | 450 | ||
512 | _enter(""); | 451 | _enter("{OBJ%x},%d", object->debug_id, event); |
452 | |||
453 | object->oob_table = fscache_osm_lookup_oob; | ||
513 | 454 | ||
514 | parent = object->parent; | ||
515 | ASSERT(parent != NULL); | 455 | ASSERT(parent != NULL); |
516 | ASSERTCMP(parent->n_ops, >, 0); | 456 | ASSERTCMP(parent->n_ops, >, 0); |
517 | ASSERTCMP(parent->n_obj_ops, >, 0); | 457 | ASSERTCMP(parent->n_obj_ops, >, 0); |
@@ -521,10 +461,8 @@ static void fscache_lookup_object(struct fscache_object *object) | |||
521 | 461 | ||
522 | if (fscache_object_is_dying(parent) || | 462 | if (fscache_object_is_dying(parent) || |
523 | test_bit(FSCACHE_IOERROR, &object->cache->flags)) { | 463 | test_bit(FSCACHE_IOERROR, &object->cache->flags)) { |
524 | _debug("unavailable"); | 464 | _leave(" [unavailable]"); |
525 | set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); | 465 | return transit_to(LOOKUP_FAILURE); |
526 | _leave(""); | ||
527 | return; | ||
528 | } | 466 | } |
529 | 467 | ||
530 | _debug("LOOKUP \"%s/%s\" in \"%s\"", | 468 | _debug("LOOKUP \"%s/%s\" in \"%s\"", |
@@ -543,10 +481,17 @@ static void fscache_lookup_object(struct fscache_object *object) | |||
543 | /* probably stuck behind another object, so move this one to | 481 | /* probably stuck behind another object, so move this one to |
544 | * the back of the queue */ | 482 | * the back of the queue */ |
545 | fscache_stat(&fscache_n_object_lookups_timed_out); | 483 | fscache_stat(&fscache_n_object_lookups_timed_out); |
546 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | 484 | _leave(" [timeout]"); |
485 | return NO_TRANSIT; | ||
547 | } | 486 | } |
548 | 487 | ||
549 | _leave(""); | 488 | if (ret < 0) { |
489 | _leave(" [error]"); | ||
490 | return transit_to(LOOKUP_FAILURE); | ||
491 | } | ||
492 | |||
493 | _leave(" [ok]"); | ||
494 | return transit_to(OBJECT_AVAILABLE); | ||
550 | } | 495 | } |
551 | 496 | ||
552 | /** | 497 | /** |
@@ -560,32 +505,20 @@ void fscache_object_lookup_negative(struct fscache_object *object) | |||
560 | { | 505 | { |
561 | struct fscache_cookie *cookie = object->cookie; | 506 | struct fscache_cookie *cookie = object->cookie; |
562 | 507 | ||
563 | _enter("{OBJ%x,%s}", | 508 | _enter("{OBJ%x,%s}", object->debug_id, object->state->name); |
564 | object->debug_id, fscache_object_states[object->state]); | ||
565 | 509 | ||
566 | spin_lock(&object->lock); | 510 | if (!test_and_set_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) { |
567 | if (object->state == FSCACHE_OBJECT_LOOKING_UP) { | ||
568 | fscache_stat(&fscache_n_object_lookups_negative); | 511 | fscache_stat(&fscache_n_object_lookups_negative); |
569 | 512 | ||
570 | /* transit here to allow write requests to begin stacking up | 513 | /* Allow write requests to begin stacking up and read requests to begin |
571 | * and read requests to begin returning ENODATA */ | 514 | * returning ENODATA. |
572 | object->state = FSCACHE_OBJECT_CREATING; | 515 | */ |
573 | spin_unlock(&object->lock); | ||
574 | |||
575 | set_bit(FSCACHE_COOKIE_PENDING_FILL, &cookie->flags); | ||
576 | set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); | 516 | set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); |
577 | 517 | ||
578 | _debug("wake up lookup %p", &cookie->flags); | 518 | _debug("wake up lookup %p", &cookie->flags); |
579 | smp_mb__before_clear_bit(); | 519 | clear_bit_unlock(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); |
580 | clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); | ||
581 | smp_mb__after_clear_bit(); | ||
582 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); | 520 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); |
583 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
584 | } else { | ||
585 | ASSERTCMP(object->state, ==, FSCACHE_OBJECT_CREATING); | ||
586 | spin_unlock(&object->lock); | ||
587 | } | 521 | } |
588 | |||
589 | _leave(""); | 522 | _leave(""); |
590 | } | 523 | } |
591 | EXPORT_SYMBOL(fscache_object_lookup_negative); | 524 | EXPORT_SYMBOL(fscache_object_lookup_negative); |
@@ -604,37 +537,30 @@ void fscache_obtained_object(struct fscache_object *object) | |||
604 | { | 537 | { |
605 | struct fscache_cookie *cookie = object->cookie; | 538 | struct fscache_cookie *cookie = object->cookie; |
606 | 539 | ||
607 | _enter("{OBJ%x,%s}", | 540 | _enter("{OBJ%x,%s}", object->debug_id, object->state->name); |
608 | object->debug_id, fscache_object_states[object->state]); | ||
609 | 541 | ||
610 | /* if we were still looking up, then we must have a positive lookup | 542 | /* if we were still looking up, then we must have a positive lookup |
611 | * result, in which case there may be data available */ | 543 | * result, in which case there may be data available */ |
612 | spin_lock(&object->lock); | 544 | if (!test_and_set_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) { |
613 | if (object->state == FSCACHE_OBJECT_LOOKING_UP) { | ||
614 | fscache_stat(&fscache_n_object_lookups_positive); | 545 | fscache_stat(&fscache_n_object_lookups_positive); |
615 | 546 | ||
616 | clear_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); | 547 | /* We do (presumably) have data */ |
617 | 548 | clear_bit_unlock(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); | |
618 | object->state = FSCACHE_OBJECT_AVAILABLE; | ||
619 | spin_unlock(&object->lock); | ||
620 | 549 | ||
621 | smp_mb__before_clear_bit(); | 550 | /* Allow write requests to begin stacking up and read requests |
622 | clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); | 551 | * to begin shovelling data. |
623 | smp_mb__after_clear_bit(); | 552 | */ |
553 | clear_bit_unlock(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); | ||
624 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); | 554 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); |
625 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
626 | } else { | 555 | } else { |
627 | ASSERTCMP(object->state, ==, FSCACHE_OBJECT_CREATING); | ||
628 | fscache_stat(&fscache_n_object_created); | 556 | fscache_stat(&fscache_n_object_created); |
629 | |||
630 | object->state = FSCACHE_OBJECT_AVAILABLE; | ||
631 | spin_unlock(&object->lock); | ||
632 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
633 | smp_wmb(); | ||
634 | } | 557 | } |
635 | 558 | ||
636 | if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, &cookie->flags)) | 559 | set_bit(FSCACHE_OBJECT_IS_AVAILABLE, &object->flags); |
637 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_CREATING); | 560 | |
561 | /* Permit __fscache_relinquish_cookie() to proceed */ | ||
562 | clear_bit_unlock(FSCACHE_COOKIE_CREATING, &cookie->flags); | ||
563 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_CREATING); | ||
638 | 564 | ||
639 | _leave(""); | 565 | _leave(""); |
640 | } | 566 | } |
@@ -643,15 +569,18 @@ EXPORT_SYMBOL(fscache_obtained_object); | |||
643 | /* | 569 | /* |
644 | * handle an object that has just become available | 570 | * handle an object that has just become available |
645 | */ | 571 | */ |
646 | static void fscache_object_available(struct fscache_object *object) | 572 | static const struct fscache_state *fscache_object_available(struct fscache_object *object, |
573 | int event) | ||
647 | { | 574 | { |
648 | _enter("{OBJ%x}", object->debug_id); | 575 | struct fscache_cookie *cookie = object->cookie; |
576 | |||
577 | _enter("{OBJ%x},%d", object->debug_id, event); | ||
578 | |||
579 | object->oob_table = fscache_osm_run_oob; | ||
649 | 580 | ||
650 | spin_lock(&object->lock); | 581 | spin_lock(&object->lock); |
651 | 582 | ||
652 | if (object->cookie && | 583 | ASSERTIF(cookie, !test_bit(FSCACHE_COOKIE_CREATING, &object->cookie->flags)); |
653 | test_and_clear_bit(FSCACHE_COOKIE_CREATING, &object->cookie->flags)) | ||
654 | wake_up_bit(&object->cookie->flags, FSCACHE_COOKIE_CREATING); | ||
655 | 584 | ||
656 | fscache_done_parent_op(object); | 585 | fscache_done_parent_op(object); |
657 | if (object->n_in_progress == 0) { | 586 | if (object->n_in_progress == 0) { |
@@ -667,72 +596,117 @@ static void fscache_object_available(struct fscache_object *object) | |||
667 | fscache_stat(&fscache_n_cop_lookup_complete); | 596 | fscache_stat(&fscache_n_cop_lookup_complete); |
668 | object->cache->ops->lookup_complete(object); | 597 | object->cache->ops->lookup_complete(object); |
669 | fscache_stat_d(&fscache_n_cop_lookup_complete); | 598 | fscache_stat_d(&fscache_n_cop_lookup_complete); |
670 | fscache_enqueue_dependents(object); | ||
671 | 599 | ||
672 | fscache_hist(fscache_obj_instantiate_histogram, object->lookup_jif); | 600 | fscache_hist(fscache_obj_instantiate_histogram, object->lookup_jif); |
673 | fscache_stat(&fscache_n_object_avail); | 601 | fscache_stat(&fscache_n_object_avail); |
674 | 602 | ||
675 | _leave(""); | 603 | _leave(""); |
604 | return transit_to(JUMPSTART_DEPS); | ||
676 | } | 605 | } |
677 | 606 | ||
678 | /* | 607 | /* |
679 | * drop an object's attachments | 608 | * Wake up this object's dependent objects now that we've become available. |
680 | */ | 609 | */ |
681 | static void fscache_drop_object(struct fscache_object *object) | 610 | static const struct fscache_state *fscache_jumpstart_dependents(struct fscache_object *object, |
611 | int event) | ||
682 | { | 612 | { |
683 | struct fscache_object *parent = object->parent; | 613 | _enter("{OBJ%x},%d", object->debug_id, event); |
684 | struct fscache_cache *cache = object->cache; | ||
685 | 614 | ||
686 | _enter("{OBJ%x,%d}", object->debug_id, object->n_children); | 615 | if (!fscache_enqueue_dependents(object, FSCACHE_OBJECT_EV_PARENT_READY)) |
616 | return NO_TRANSIT; /* Not finished; requeue */ | ||
617 | return transit_to(WAIT_FOR_CMD); | ||
618 | } | ||
687 | 619 | ||
688 | ASSERTCMP(object->cookie, ==, NULL); | 620 | /* |
689 | ASSERT(hlist_unhashed(&object->cookie_link)); | 621 | * Handle lookup or creation failute. |
622 | */ | ||
623 | static const struct fscache_state *fscache_lookup_failure(struct fscache_object *object, | ||
624 | int event) | ||
625 | { | ||
626 | struct fscache_cookie *cookie; | ||
627 | bool wake_looking_up = false; | ||
690 | 628 | ||
691 | spin_lock(&cache->object_list_lock); | 629 | _enter("{OBJ%x},%d", object->debug_id, event); |
692 | list_del_init(&object->cache_link); | ||
693 | spin_unlock(&cache->object_list_lock); | ||
694 | 630 | ||
695 | fscache_stat(&fscache_n_cop_drop_object); | 631 | object->oob_event_mask = 0; |
696 | cache->ops->drop_object(object); | ||
697 | fscache_stat_d(&fscache_n_cop_drop_object); | ||
698 | 632 | ||
699 | if (parent) { | 633 | fscache_stat(&fscache_n_cop_lookup_complete); |
700 | _debug("release parent OBJ%x {%d}", | 634 | object->cache->ops->lookup_complete(object); |
701 | parent->debug_id, parent->n_children); | 635 | fscache_stat_d(&fscache_n_cop_lookup_complete); |
702 | 636 | ||
703 | spin_lock(&parent->lock); | 637 | spin_lock(&object->lock); |
704 | parent->n_children--; | 638 | cookie = object->cookie; |
705 | if (parent->n_children == 0) | 639 | set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); |
706 | fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); | 640 | if (cookie) { |
707 | spin_unlock(&parent->lock); | 641 | if (test_and_clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags)) |
708 | object->parent = NULL; | 642 | wake_looking_up = true; |
643 | clear_bit_unlock(FSCACHE_COOKIE_CREATING, &cookie->flags); | ||
709 | } | 644 | } |
645 | spin_unlock(&object->lock); | ||
710 | 646 | ||
711 | /* this just shifts the object release to the work processor */ | 647 | if (wake_looking_up) |
712 | fscache_put_object(object); | 648 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); |
649 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_CREATING); | ||
713 | 650 | ||
714 | _leave(""); | 651 | fscache_done_parent_op(object); |
652 | return transit_to(KILL_OBJECT); | ||
653 | } | ||
654 | |||
655 | /* | ||
656 | * Wait for completion of all active operations on this object and the death of | ||
657 | * all child objects of this object. | ||
658 | */ | ||
659 | static const struct fscache_state *fscache_kill_object(struct fscache_object *object, | ||
660 | int event) | ||
661 | { | ||
662 | _enter("{OBJ%x,%d,%d},%d", | ||
663 | object->debug_id, object->n_ops, object->n_children, event); | ||
664 | |||
665 | object->oob_event_mask = 0; | ||
666 | |||
667 | spin_lock(&object->lock); | ||
668 | clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); | ||
669 | spin_unlock(&object->lock); | ||
670 | |||
671 | if (list_empty(&object->dependents) && | ||
672 | object->n_ops == 0 && | ||
673 | object->n_children == 0) | ||
674 | return object->cookie ? | ||
675 | transit_to(DETACH_FROM_COOKIE) : transit_to(DROP_OBJECT); | ||
676 | |||
677 | spin_lock(&object->lock); | ||
678 | fscache_start_operations(object); | ||
679 | spin_unlock(&object->lock); | ||
680 | |||
681 | if (!list_empty(&object->dependents)) | ||
682 | return transit_to(KILL_DEPENDENTS); | ||
683 | |||
684 | return transit_to(WAIT_FOR_CLEARANCE); | ||
715 | } | 685 | } |
716 | 686 | ||
717 | /* | 687 | /* |
718 | * release or recycle an object that the netfs has discarded | 688 | * Kill dependent objects. |
719 | */ | 689 | */ |
720 | static void fscache_release_object(struct fscache_object *object) | 690 | static const struct fscache_state *fscache_kill_dependents(struct fscache_object *object, |
691 | int event) | ||
721 | { | 692 | { |
722 | _enter(""); | 693 | _enter("{OBJ%x},%d", object->debug_id, event); |
723 | 694 | ||
724 | fscache_drop_object(object); | 695 | if (!fscache_enqueue_dependents(object, FSCACHE_OBJECT_EV_KILL)) |
696 | return NO_TRANSIT; /* Not finished */ | ||
697 | return transit_to(WAIT_FOR_CLEARANCE); | ||
725 | } | 698 | } |
726 | 699 | ||
727 | /* | 700 | /* |
728 | * withdraw an object from active service | 701 | * withdraw an object from active service |
729 | */ | 702 | */ |
730 | static void fscache_withdraw_object(struct fscache_object *object) | 703 | static const struct fscache_state *fscache_detach_from_cookie(struct fscache_object *object, |
704 | int event) | ||
731 | { | 705 | { |
732 | struct fscache_cookie *cookie; | 706 | struct fscache_cookie *cookie; |
733 | bool detached; | 707 | bool detached = false, awaken = false; |
734 | 708 | ||
735 | _enter(""); | 709 | _enter("{OBJ%x},%d", object->debug_id, event); |
736 | 710 | ||
737 | spin_lock(&object->lock); | 711 | spin_lock(&object->lock); |
738 | cookie = object->cookie; | 712 | cookie = object->cookie; |
@@ -742,14 +716,15 @@ static void fscache_withdraw_object(struct fscache_object *object) | |||
742 | atomic_inc(&cookie->usage); | 716 | atomic_inc(&cookie->usage); |
743 | spin_unlock(&object->lock); | 717 | spin_unlock(&object->lock); |
744 | 718 | ||
745 | detached = false; | ||
746 | spin_lock(&cookie->lock); | 719 | spin_lock(&cookie->lock); |
747 | spin_lock(&object->lock); | 720 | spin_lock(&object->lock); |
748 | 721 | ||
749 | if (object->cookie == cookie) { | 722 | if (object->cookie == cookie) { |
750 | hlist_del_init(&object->cookie_link); | 723 | hlist_del_init(&object->cookie_link); |
751 | object->cookie = NULL; | 724 | object->cookie = NULL; |
752 | fscache_invalidation_complete(cookie); | 725 | if (test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, |
726 | &cookie->flags)) | ||
727 | awaken = true; | ||
753 | detached = true; | 728 | detached = true; |
754 | } | 729 | } |
755 | spin_unlock(&cookie->lock); | 730 | spin_unlock(&cookie->lock); |
@@ -760,37 +735,62 @@ static void fscache_withdraw_object(struct fscache_object *object) | |||
760 | 735 | ||
761 | spin_unlock(&object->lock); | 736 | spin_unlock(&object->lock); |
762 | 737 | ||
763 | fscache_drop_object(object); | 738 | if (awaken) |
739 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); | ||
740 | |||
741 | fscache_stat(&fscache_n_object_dead); | ||
742 | _leave(""); | ||
743 | return transit_to(DROP_OBJECT); | ||
764 | } | 744 | } |
765 | 745 | ||
766 | /* | 746 | /* |
767 | * withdraw an object from active service at the behest of the cache | 747 | * Drop an object's attachments |
768 | * - need break the links to a cached object cookie | 748 | */ |
769 | * - called under two situations: | 749 | static const struct fscache_state *fscache_drop_object(struct fscache_object *object, |
770 | * (1) recycler decides to reclaim an in-use object | 750 | int event) |
771 | * (2) a cache is unmounted | ||
772 | * - have to take care as the cookie can be being relinquished by the netfs | ||
773 | * simultaneously | ||
774 | * - the object is pinned by the caller holding a refcount on it | ||
775 | */ | ||
776 | void fscache_withdrawing_object(struct fscache_cache *cache, | ||
777 | struct fscache_object *object) | ||
778 | { | 751 | { |
779 | bool enqueue = false; | 752 | struct fscache_object *parent = object->parent; |
753 | struct fscache_cache *cache = object->cache; | ||
780 | 754 | ||
781 | _enter(",OBJ%x", object->debug_id); | 755 | _enter("{OBJ%x,%d},%d", object->debug_id, object->n_children, event); |
782 | 756 | ||
757 | ASSERTCMP(object->cookie, ==, NULL); | ||
758 | ASSERT(hlist_unhashed(&object->cookie_link)); | ||
759 | |||
760 | /* Prevent a race with our last child, which has to signal EV_CLEARED | ||
761 | * before dropping our spinlock. | ||
762 | */ | ||
783 | spin_lock(&object->lock); | 763 | spin_lock(&object->lock); |
784 | if (object->state < FSCACHE_OBJECT_WITHDRAWING) { | ||
785 | object->state = FSCACHE_OBJECT_WITHDRAWING; | ||
786 | enqueue = true; | ||
787 | } | ||
788 | spin_unlock(&object->lock); | 764 | spin_unlock(&object->lock); |
789 | 765 | ||
790 | if (enqueue) | 766 | /* Discard from the cache's collection of objects */ |
791 | fscache_enqueue_object(object); | 767 | spin_lock(&cache->object_list_lock); |
768 | list_del_init(&object->cache_link); | ||
769 | spin_unlock(&cache->object_list_lock); | ||
770 | |||
771 | fscache_stat(&fscache_n_cop_drop_object); | ||
772 | cache->ops->drop_object(object); | ||
773 | fscache_stat_d(&fscache_n_cop_drop_object); | ||
774 | |||
775 | /* The parent object wants to know when all it dependents have gone */ | ||
776 | if (parent) { | ||
777 | _debug("release parent OBJ%x {%d}", | ||
778 | parent->debug_id, parent->n_children); | ||
779 | |||
780 | spin_lock(&parent->lock); | ||
781 | parent->n_children--; | ||
782 | if (parent->n_children == 0) | ||
783 | fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); | ||
784 | spin_unlock(&parent->lock); | ||
785 | object->parent = NULL; | ||
786 | } | ||
787 | |||
788 | /* this just shifts the object release to the work processor */ | ||
789 | fscache_put_object(object); | ||
790 | fscache_stat(&fscache_n_object_dead); | ||
792 | 791 | ||
793 | _leave(""); | 792 | _leave(""); |
793 | return transit_to(OBJECT_DEAD); | ||
794 | } | 794 | } |
795 | 795 | ||
796 | /* | 796 | /* |
@@ -807,7 +807,7 @@ static int fscache_get_object(struct fscache_object *object) | |||
807 | } | 807 | } |
808 | 808 | ||
809 | /* | 809 | /* |
810 | * discard a ref on a work item | 810 | * Discard a ref on an object |
811 | */ | 811 | */ |
812 | static void fscache_put_object(struct fscache_object *object) | 812 | static void fscache_put_object(struct fscache_object *object) |
813 | { | 813 | { |
@@ -839,7 +839,7 @@ void fscache_enqueue_object(struct fscache_object *object) | |||
839 | 839 | ||
840 | /** | 840 | /** |
841 | * fscache_object_sleep_till_congested - Sleep until object wq is congested | 841 | * fscache_object_sleep_till_congested - Sleep until object wq is congested |
842 | * @timoutp: Scheduler sleep timeout | 842 | * @timeoutp: Scheduler sleep timeout |
843 | * | 843 | * |
844 | * Allow an object handler to sleep until the object workqueue is congested. | 844 | * Allow an object handler to sleep until the object workqueue is congested. |
845 | * | 845 | * |
@@ -867,18 +867,21 @@ bool fscache_object_sleep_till_congested(signed long *timeoutp) | |||
867 | EXPORT_SYMBOL_GPL(fscache_object_sleep_till_congested); | 867 | EXPORT_SYMBOL_GPL(fscache_object_sleep_till_congested); |
868 | 868 | ||
869 | /* | 869 | /* |
870 | * enqueue the dependents of an object for metadata-type processing | 870 | * Enqueue the dependents of an object for metadata-type processing. |
871 | * - the caller must hold the object's lock | 871 | * |
872 | * - this may cause an already locked object to wind up being processed again | 872 | * If we don't manage to finish the list before the scheduler wants to run |
873 | * again then return false immediately. We return true if the list was | ||
874 | * cleared. | ||
873 | */ | 875 | */ |
874 | static void fscache_enqueue_dependents(struct fscache_object *object) | 876 | static bool fscache_enqueue_dependents(struct fscache_object *object, int event) |
875 | { | 877 | { |
876 | struct fscache_object *dep; | 878 | struct fscache_object *dep; |
879 | bool ret = true; | ||
877 | 880 | ||
878 | _enter("{OBJ%x}", object->debug_id); | 881 | _enter("{OBJ%x}", object->debug_id); |
879 | 882 | ||
880 | if (list_empty(&object->dependents)) | 883 | if (list_empty(&object->dependents)) |
881 | return; | 884 | return true; |
882 | 885 | ||
883 | spin_lock(&object->lock); | 886 | spin_lock(&object->lock); |
884 | 887 | ||
@@ -887,23 +890,23 @@ static void fscache_enqueue_dependents(struct fscache_object *object) | |||
887 | struct fscache_object, dep_link); | 890 | struct fscache_object, dep_link); |
888 | list_del_init(&dep->dep_link); | 891 | list_del_init(&dep->dep_link); |
889 | 892 | ||
890 | 893 | fscache_raise_event(dep, event); | |
891 | /* sort onto appropriate lists */ | ||
892 | fscache_enqueue_object(dep); | ||
893 | fscache_put_object(dep); | 894 | fscache_put_object(dep); |
894 | 895 | ||
895 | if (!list_empty(&object->dependents)) | 896 | if (!list_empty(&object->dependents) && need_resched()) { |
896 | cond_resched_lock(&object->lock); | 897 | ret = false; |
898 | break; | ||
899 | } | ||
897 | } | 900 | } |
898 | 901 | ||
899 | spin_unlock(&object->lock); | 902 | spin_unlock(&object->lock); |
903 | return ret; | ||
900 | } | 904 | } |
901 | 905 | ||
902 | /* | 906 | /* |
903 | * remove an object from whatever queue it's waiting on | 907 | * remove an object from whatever queue it's waiting on |
904 | * - the caller must hold object->lock | ||
905 | */ | 908 | */ |
906 | void fscache_dequeue_object(struct fscache_object *object) | 909 | static void fscache_dequeue_object(struct fscache_object *object) |
907 | { | 910 | { |
908 | _enter("{OBJ%x}", object->debug_id); | 911 | _enter("{OBJ%x}", object->debug_id); |
909 | 912 | ||
@@ -963,12 +966,14 @@ EXPORT_SYMBOL(fscache_check_aux); | |||
963 | /* | 966 | /* |
964 | * Asynchronously invalidate an object. | 967 | * Asynchronously invalidate an object. |
965 | */ | 968 | */ |
966 | static void fscache_invalidate_object(struct fscache_object *object) | 969 | static const struct fscache_state *_fscache_invalidate_object(struct fscache_object *object, |
970 | int event) | ||
967 | { | 971 | { |
968 | struct fscache_operation *op; | 972 | struct fscache_operation *op; |
969 | struct fscache_cookie *cookie = object->cookie; | 973 | struct fscache_cookie *cookie = object->cookie; |
970 | 974 | ||
971 | _enter("{OBJ%x}", object->debug_id); | 975 | _enter("{OBJ%x},%d", object->debug_id, event); |
976 | |||
972 | 977 | ||
973 | /* Reject any new read/write ops and abort any that are pending. */ | 978 | /* Reject any new read/write ops and abort any that are pending. */ |
974 | fscache_invalidate_writes(cookie); | 979 | fscache_invalidate_writes(cookie); |
@@ -978,9 +983,9 @@ static void fscache_invalidate_object(struct fscache_object *object) | |||
978 | /* Now we have to wait for in-progress reads and writes */ | 983 | /* Now we have to wait for in-progress reads and writes */ |
979 | op = kzalloc(sizeof(*op), GFP_KERNEL); | 984 | op = kzalloc(sizeof(*op), GFP_KERNEL); |
980 | if (!op) { | 985 | if (!op) { |
981 | fscache_raise_event(object, FSCACHE_OBJECT_EV_ERROR); | 986 | clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); |
982 | _leave(" [ENOMEM]"); | 987 | _leave(" [ENOMEM]"); |
983 | return; | 988 | return transit_to(KILL_OBJECT); |
984 | } | 989 | } |
985 | 990 | ||
986 | fscache_operation_init(op, object->cache->ops->invalidate_object, NULL); | 991 | fscache_operation_init(op, object->cache->ops->invalidate_object, NULL); |
@@ -1001,13 +1006,44 @@ static void fscache_invalidate_object(struct fscache_object *object) | |||
1001 | /* We can allow read and write requests to come in once again. They'll | 1006 | /* We can allow read and write requests to come in once again. They'll |
1002 | * queue up behind our exclusive invalidation operation. | 1007 | * queue up behind our exclusive invalidation operation. |
1003 | */ | 1008 | */ |
1004 | fscache_invalidation_complete(cookie); | 1009 | if (test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) |
1005 | _leave(""); | 1010 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); |
1006 | return; | 1011 | _leave(" [ok]"); |
1012 | return transit_to(UPDATE_OBJECT); | ||
1007 | 1013 | ||
1008 | submit_op_failed: | 1014 | submit_op_failed: |
1015 | clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); | ||
1009 | spin_unlock(&cookie->lock); | 1016 | spin_unlock(&cookie->lock); |
1010 | kfree(op); | 1017 | kfree(op); |
1011 | fscache_raise_event(object, FSCACHE_OBJECT_EV_ERROR); | ||
1012 | _leave(" [EIO]"); | 1018 | _leave(" [EIO]"); |
1019 | return transit_to(KILL_OBJECT); | ||
1020 | } | ||
1021 | |||
1022 | static const struct fscache_state *fscache_invalidate_object(struct fscache_object *object, | ||
1023 | int event) | ||
1024 | { | ||
1025 | const struct fscache_state *s; | ||
1026 | |||
1027 | fscache_stat(&fscache_n_invalidates_run); | ||
1028 | fscache_stat(&fscache_n_cop_invalidate_object); | ||
1029 | s = _fscache_invalidate_object(object, event); | ||
1030 | fscache_stat_d(&fscache_n_cop_invalidate_object); | ||
1031 | return s; | ||
1032 | } | ||
1033 | |||
1034 | /* | ||
1035 | * Asynchronously update an object. | ||
1036 | */ | ||
1037 | static const struct fscache_state *fscache_update_object(struct fscache_object *object, | ||
1038 | int event) | ||
1039 | { | ||
1040 | _enter("{OBJ%x},%d", object->debug_id, event); | ||
1041 | |||
1042 | fscache_stat(&fscache_n_updates_run); | ||
1043 | fscache_stat(&fscache_n_cop_update_object); | ||
1044 | object->cache->ops->update_object(object); | ||
1045 | fscache_stat_d(&fscache_n_cop_update_object); | ||
1046 | |||
1047 | _leave(""); | ||
1048 | return transit_to(WAIT_FOR_CMD); | ||
1013 | } | 1049 | } |
diff --git a/fs/fscache/operation.c b/fs/fscache/operation.c index ccf02194e7a6..4da211b21ddf 100644 --- a/fs/fscache/operation.c +++ b/fs/fscache/operation.c | |||
@@ -119,7 +119,7 @@ int fscache_submit_exclusive_op(struct fscache_object *object, | |||
119 | /* need to issue a new write op after this */ | 119 | /* need to issue a new write op after this */ |
120 | clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); | 120 | clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); |
121 | ret = 0; | 121 | ret = 0; |
122 | } else if (object->state == FSCACHE_OBJECT_CREATING) { | 122 | } else if (test_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) { |
123 | op->object = object; | 123 | op->object = object; |
124 | object->n_ops++; | 124 | object->n_ops++; |
125 | object->n_exclusive++; /* reads and writes must wait */ | 125 | object->n_exclusive++; /* reads and writes must wait */ |
@@ -144,7 +144,7 @@ int fscache_submit_exclusive_op(struct fscache_object *object, | |||
144 | */ | 144 | */ |
145 | static void fscache_report_unexpected_submission(struct fscache_object *object, | 145 | static void fscache_report_unexpected_submission(struct fscache_object *object, |
146 | struct fscache_operation *op, | 146 | struct fscache_operation *op, |
147 | unsigned long ostate) | 147 | const struct fscache_state *ostate) |
148 | { | 148 | { |
149 | static bool once_only; | 149 | static bool once_only; |
150 | struct fscache_operation *p; | 150 | struct fscache_operation *p; |
@@ -155,11 +155,8 @@ static void fscache_report_unexpected_submission(struct fscache_object *object, | |||
155 | once_only = true; | 155 | once_only = true; |
156 | 156 | ||
157 | kdebug("unexpected submission OP%x [OBJ%x %s]", | 157 | kdebug("unexpected submission OP%x [OBJ%x %s]", |
158 | op->debug_id, object->debug_id, | 158 | op->debug_id, object->debug_id, object->state->name); |
159 | fscache_object_states[object->state]); | 159 | kdebug("objstate=%s [%s]", object->state->name, ostate->name); |
160 | kdebug("objstate=%s [%s]", | ||
161 | fscache_object_states[object->state], | ||
162 | fscache_object_states[ostate]); | ||
163 | kdebug("objflags=%lx", object->flags); | 160 | kdebug("objflags=%lx", object->flags); |
164 | kdebug("objevent=%lx [%lx]", object->events, object->event_mask); | 161 | kdebug("objevent=%lx [%lx]", object->events, object->event_mask); |
165 | kdebug("ops=%u inp=%u exc=%u", | 162 | kdebug("ops=%u inp=%u exc=%u", |
@@ -190,7 +187,7 @@ static void fscache_report_unexpected_submission(struct fscache_object *object, | |||
190 | int fscache_submit_op(struct fscache_object *object, | 187 | int fscache_submit_op(struct fscache_object *object, |
191 | struct fscache_operation *op) | 188 | struct fscache_operation *op) |
192 | { | 189 | { |
193 | unsigned long ostate; | 190 | const struct fscache_state *ostate; |
194 | int ret; | 191 | int ret; |
195 | 192 | ||
196 | _enter("{OBJ%x OP%x},{%u}", | 193 | _enter("{OBJ%x OP%x},{%u}", |
@@ -226,16 +223,14 @@ int fscache_submit_op(struct fscache_object *object, | |||
226 | fscache_run_op(object, op); | 223 | fscache_run_op(object, op); |
227 | } | 224 | } |
228 | ret = 0; | 225 | ret = 0; |
229 | } else if (object->state == FSCACHE_OBJECT_CREATING) { | 226 | } else if (test_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) { |
230 | op->object = object; | 227 | op->object = object; |
231 | object->n_ops++; | 228 | object->n_ops++; |
232 | atomic_inc(&op->usage); | 229 | atomic_inc(&op->usage); |
233 | list_add_tail(&op->pend_link, &object->pending_ops); | 230 | list_add_tail(&op->pend_link, &object->pending_ops); |
234 | fscache_stat(&fscache_n_op_pend); | 231 | fscache_stat(&fscache_n_op_pend); |
235 | ret = 0; | 232 | ret = 0; |
236 | } else if (object->state == FSCACHE_OBJECT_DYING || | 233 | } else if (fscache_object_is_dying(object)) { |
237 | object->state == FSCACHE_OBJECT_LC_DYING || | ||
238 | object->state == FSCACHE_OBJECT_WITHDRAWING) { | ||
239 | fscache_stat(&fscache_n_op_rejected); | 234 | fscache_stat(&fscache_n_op_rejected); |
240 | op->state = FSCACHE_OP_ST_CANCELLED; | 235 | op->state = FSCACHE_OP_ST_CANCELLED; |
241 | ret = -ENOBUFS; | 236 | ret = -ENOBUFS; |
@@ -266,13 +261,14 @@ void fscache_abort_object(struct fscache_object *object) | |||
266 | 261 | ||
267 | /* | 262 | /* |
268 | * jump start the operation processing on an object | 263 | * jump start the operation processing on an object |
269 | * - caller must hold object->lock | ||
270 | */ | 264 | */ |
271 | void fscache_start_operations(struct fscache_object *object) | 265 | void fscache_start_operations(struct fscache_object *object) |
272 | { | 266 | { |
273 | struct fscache_operation *op; | 267 | struct fscache_operation *op; |
274 | bool stop = false; | 268 | bool stop = false; |
275 | 269 | ||
270 | ASSERT(spin_is_locked(&object->lock)); | ||
271 | |||
276 | while (!list_empty(&object->pending_ops) && !stop) { | 272 | while (!list_empty(&object->pending_ops) && !stop) { |
277 | op = list_entry(object->pending_ops.next, | 273 | op = list_entry(object->pending_ops.next, |
278 | struct fscache_operation, pend_link); | 274 | struct fscache_operation, pend_link); |
diff --git a/fs/fscache/page.c b/fs/fscache/page.c index 42f8f2d8a197..b4e4b424160a 100644 --- a/fs/fscache/page.c +++ b/fs/fscache/page.c | |||
@@ -408,7 +408,7 @@ int __fscache_read_or_alloc_page(struct fscache_cookie *cookie, | |||
408 | object = hlist_entry(cookie->backing_objects.first, | 408 | object = hlist_entry(cookie->backing_objects.first, |
409 | struct fscache_object, cookie_link); | 409 | struct fscache_object, cookie_link); |
410 | 410 | ||
411 | ASSERTCMP(object->state, >, FSCACHE_OBJECT_LOOKING_UP); | 411 | ASSERT(test_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)); |
412 | 412 | ||
413 | atomic_inc(&object->n_reads); | 413 | atomic_inc(&object->n_reads); |
414 | __set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags); | 414 | __set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags); |
@@ -729,8 +729,9 @@ static void fscache_write_op(struct fscache_operation *_op) | |||
729 | */ | 729 | */ |
730 | spin_unlock(&object->lock); | 730 | spin_unlock(&object->lock); |
731 | fscache_op_complete(&op->op, false); | 731 | fscache_op_complete(&op->op, false); |
732 | _leave(" [cancel] op{f=%lx s=%u} obj{s=%u f=%lx}", | 732 | _leave(" [cancel] op{f=%lx s=%u} obj{s=%s f=%lx}", |
733 | _op->flags, _op->state, object->state, object->flags); | 733 | _op->flags, _op->state, object->state->short_name, |
734 | object->flags); | ||
734 | return; | 735 | return; |
735 | } | 736 | } |
736 | 737 | ||
@@ -833,14 +834,12 @@ void fscache_invalidate_writes(struct fscache_cookie *cookie) | |||
833 | * (1) negative lookup, object not yet created (FSCACHE_COOKIE_CREATING is | 834 | * (1) negative lookup, object not yet created (FSCACHE_COOKIE_CREATING is |
834 | * set) | 835 | * set) |
835 | * | 836 | * |
836 | * (a) no writes yet (set FSCACHE_COOKIE_PENDING_FILL and queue deferred | 837 | * (a) no writes yet |
837 | * fill op) | ||
838 | * | 838 | * |
839 | * (b) writes deferred till post-creation (mark page for writing and | 839 | * (b) writes deferred till post-creation (mark page for writing and |
840 | * return immediately) | 840 | * return immediately) |
841 | * | 841 | * |
842 | * (2) negative lookup, object created, initial fill being made from netfs | 842 | * (2) negative lookup, object created, initial fill being made from netfs |
843 | * (FSCACHE_COOKIE_INITIAL_FILL is set) | ||
844 | * | 843 | * |
845 | * (a) fill point not yet reached this page (mark page for writing and | 844 | * (a) fill point not yet reached this page (mark page for writing and |
846 | * return) | 845 | * return) |