diff options
Diffstat (limited to 'fs/fscache/object.c')
-rw-r--r-- | fs/fscache/object.c | 1106 |
1 files changed, 570 insertions, 536 deletions
diff --git a/fs/fscache/object.c b/fs/fscache/object.c index 50d41c180211..86d75a60b20c 100644 --- a/fs/fscache/object.c +++ b/fs/fscache/object.c | |||
@@ -15,52 +15,131 @@ | |||
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 | |
33 | [FSCACHE_OBJECT_WITHDRAWING] = "OBJECT_WITHDRAWING", | 34 | #define __STATE_NAME(n) fscache_osm_##n |
34 | [FSCACHE_OBJECT_DEAD] = "OBJECT_DEAD", | 35 | #define STATE(n) (&__STATE_NAME(n)) |
36 | |||
37 | /* | ||
38 | * Define a work state. Work states are execution states. No event processing | ||
39 | * is performed by them. The function attached to a work state returns a | ||
40 | * pointer indicating the next state to which the state machine should | ||
41 | * transition. Returning NO_TRANSIT repeats the current state, but goes back | ||
42 | * to the scheduler first. | ||
43 | */ | ||
44 | #define WORK_STATE(n, sn, f) \ | ||
45 | const struct fscache_state __STATE_NAME(n) = { \ | ||
46 | .name = #n, \ | ||
47 | .short_name = sn, \ | ||
48 | .work = f \ | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * Returns from work states. | ||
53 | */ | ||
54 | #define transit_to(state) ({ prefetch(&STATE(state)->work); STATE(state); }) | ||
55 | |||
56 | #define NO_TRANSIT ((struct fscache_state *)NULL) | ||
57 | |||
58 | /* | ||
59 | * Define a wait state. Wait states are event processing states. No execution | ||
60 | * is performed by them. Wait states are just tables of "if event X occurs, | ||
61 | * clear it and transition to state Y". The dispatcher returns to the | ||
62 | * scheduler if none of the events in which the wait state has an interest are | ||
63 | * currently pending. | ||
64 | */ | ||
65 | #define WAIT_STATE(n, sn, ...) \ | ||
66 | const struct fscache_state __STATE_NAME(n) = { \ | ||
67 | .name = #n, \ | ||
68 | .short_name = sn, \ | ||
69 | .work = NULL, \ | ||
70 | .transitions = { __VA_ARGS__, { 0, NULL } } \ | ||
71 | } | ||
72 | |||
73 | #define TRANSIT_TO(state, emask) \ | ||
74 | { .events = (emask), .transit_to = STATE(state) } | ||
75 | |||
76 | /* | ||
77 | * The object state machine. | ||
78 | */ | ||
79 | static WORK_STATE(INIT_OBJECT, "INIT", fscache_initialise_object); | ||
80 | static WORK_STATE(PARENT_READY, "PRDY", fscache_parent_ready); | ||
81 | static WORK_STATE(ABORT_INIT, "ABRT", fscache_abort_initialisation); | ||
82 | static WORK_STATE(LOOK_UP_OBJECT, "LOOK", fscache_look_up_object); | ||
83 | static WORK_STATE(CREATE_OBJECT, "CRTO", fscache_look_up_object); | ||
84 | static WORK_STATE(OBJECT_AVAILABLE, "AVBL", fscache_object_available); | ||
85 | static WORK_STATE(JUMPSTART_DEPS, "JUMP", fscache_jumpstart_dependents); | ||
86 | |||
87 | static WORK_STATE(INVALIDATE_OBJECT, "INVL", fscache_invalidate_object); | ||
88 | static WORK_STATE(UPDATE_OBJECT, "UPDT", fscache_update_object); | ||
89 | |||
90 | static WORK_STATE(LOOKUP_FAILURE, "LCFL", fscache_lookup_failure); | ||
91 | static WORK_STATE(KILL_OBJECT, "KILL", fscache_kill_object); | ||
92 | static WORK_STATE(KILL_DEPENDENTS, "KDEP", fscache_kill_dependents); | ||
93 | static WORK_STATE(DROP_OBJECT, "DROP", fscache_drop_object); | ||
94 | static WORK_STATE(OBJECT_DEAD, "DEAD", (void*)2UL); | ||
95 | |||
96 | static WAIT_STATE(WAIT_FOR_INIT, "?INI", | ||
97 | TRANSIT_TO(INIT_OBJECT, 1 << FSCACHE_OBJECT_EV_NEW_CHILD)); | ||
98 | |||
99 | static WAIT_STATE(WAIT_FOR_PARENT, "?PRN", | ||
100 | TRANSIT_TO(PARENT_READY, 1 << FSCACHE_OBJECT_EV_PARENT_READY)); | ||
101 | |||
102 | static WAIT_STATE(WAIT_FOR_CMD, "?CMD", | ||
103 | TRANSIT_TO(INVALIDATE_OBJECT, 1 << FSCACHE_OBJECT_EV_INVALIDATE), | ||
104 | TRANSIT_TO(UPDATE_OBJECT, 1 << FSCACHE_OBJECT_EV_UPDATE), | ||
105 | TRANSIT_TO(JUMPSTART_DEPS, 1 << FSCACHE_OBJECT_EV_NEW_CHILD)); | ||
106 | |||
107 | static WAIT_STATE(WAIT_FOR_CLEARANCE, "?CLR", | ||
108 | TRANSIT_TO(KILL_OBJECT, 1 << FSCACHE_OBJECT_EV_CLEARED)); | ||
109 | |||
110 | /* | ||
111 | * Out-of-band event transition tables. These are for handling unexpected | ||
112 | * events, such as an I/O error. If an OOB event occurs, the state machine | ||
113 | * clears and disables the event and forces a transition to the nominated work | ||
114 | * state (acurrently executing work states will complete first). | ||
115 | * | ||
116 | * In such a situation, object->state remembers the state the machine should | ||
117 | * have been in/gone to and returning NO_TRANSIT returns to that. | ||
118 | */ | ||
119 | static const struct fscache_transition fscache_osm_init_oob[] = { | ||
120 | TRANSIT_TO(ABORT_INIT, | ||
121 | (1 << FSCACHE_OBJECT_EV_ERROR) | | ||
122 | (1 << FSCACHE_OBJECT_EV_KILL)), | ||
123 | { 0, NULL } | ||
124 | }; | ||
125 | |||
126 | static const struct fscache_transition fscache_osm_lookup_oob[] = { | ||
127 | TRANSIT_TO(LOOKUP_FAILURE, | ||
128 | (1 << FSCACHE_OBJECT_EV_ERROR) | | ||
129 | (1 << FSCACHE_OBJECT_EV_KILL)), | ||
130 | { 0, NULL } | ||
35 | }; | 131 | }; |
36 | EXPORT_SYMBOL(fscache_object_states); | 132 | |
37 | 133 | static const struct fscache_transition fscache_osm_run_oob[] = { | |
38 | const char fscache_object_states_short[FSCACHE_OBJECT__NSTATES][5] = { | 134 | TRANSIT_TO(KILL_OBJECT, |
39 | [FSCACHE_OBJECT_INIT] = "INIT", | 135 | (1 << FSCACHE_OBJECT_EV_ERROR) | |
40 | [FSCACHE_OBJECT_LOOKING_UP] = "LOOK", | 136 | (1 << FSCACHE_OBJECT_EV_KILL)), |
41 | [FSCACHE_OBJECT_CREATING] = "CRTN", | 137 | { 0, NULL } |
42 | [FSCACHE_OBJECT_AVAILABLE] = "AVBL", | ||
43 | [FSCACHE_OBJECT_ACTIVE] = "ACTV", | ||
44 | [FSCACHE_OBJECT_INVALIDATING] = "INVL", | ||
45 | [FSCACHE_OBJECT_UPDATING] = "UPDT", | ||
46 | [FSCACHE_OBJECT_DYING] = "DYNG", | ||
47 | [FSCACHE_OBJECT_LC_DYING] = "LCDY", | ||
48 | [FSCACHE_OBJECT_ABORT_INIT] = "ABTI", | ||
49 | [FSCACHE_OBJECT_RELEASING] = "RELS", | ||
50 | [FSCACHE_OBJECT_RECYCLING] = "RCYC", | ||
51 | [FSCACHE_OBJECT_WITHDRAWING] = "WTHD", | ||
52 | [FSCACHE_OBJECT_DEAD] = "DEAD", | ||
53 | }; | 138 | }; |
54 | 139 | ||
55 | static int fscache_get_object(struct fscache_object *); | 140 | static int fscache_get_object(struct fscache_object *); |
56 | static void fscache_put_object(struct fscache_object *); | 141 | static void fscache_put_object(struct fscache_object *); |
57 | static void fscache_initialise_object(struct fscache_object *); | 142 | 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 *); | 143 | static void fscache_dequeue_object(struct fscache_object *); |
65 | 144 | ||
66 | /* | 145 | /* |
@@ -75,295 +154,116 @@ static inline void fscache_done_parent_op(struct fscache_object *object) | |||
75 | object->debug_id, parent->debug_id, parent->n_ops); | 154 | object->debug_id, parent->debug_id, parent->n_ops); |
76 | 155 | ||
77 | spin_lock_nested(&parent->lock, 1); | 156 | spin_lock_nested(&parent->lock, 1); |
78 | parent->n_ops--; | ||
79 | parent->n_obj_ops--; | 157 | parent->n_obj_ops--; |
158 | parent->n_ops--; | ||
80 | if (parent->n_ops == 0) | 159 | if (parent->n_ops == 0) |
81 | fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); | 160 | fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); |
82 | spin_unlock(&parent->lock); | 161 | spin_unlock(&parent->lock); |
83 | } | 162 | } |
84 | 163 | ||
85 | /* | 164 | /* |
86 | * Notify netfs of invalidation completion. | 165 | * Object state machine dispatcher. |
87 | */ | 166 | */ |
88 | static inline void fscache_invalidation_complete(struct fscache_cookie *cookie) | 167 | static void fscache_object_sm_dispatcher(struct fscache_object *object) |
89 | { | 168 | { |
90 | if (test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) | 169 | const struct fscache_transition *t; |
91 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); | 170 | const struct fscache_state *state, *new_state; |
92 | } | 171 | unsigned long events, event_mask; |
93 | 172 | 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 | 173 | ||
108 | ASSERT(object != NULL); | 174 | ASSERT(object != NULL); |
109 | 175 | ||
110 | _enter("{OBJ%x,%s,%lx}", | 176 | _enter("{OBJ%x,%s,%lx}", |
111 | object->debug_id, fscache_object_states[object->state], | 177 | object->debug_id, object->state->name, object->events); |
112 | object->events); | 178 | |
113 | 179 | event_mask = object->event_mask; | |
114 | switch (object->state) { | 180 | restart: |
115 | /* wait for the parent object to become ready */ | 181 | object->event_mask = 0; /* Mask normal event handling */ |
116 | case FSCACHE_OBJECT_INIT: | 182 | state = object->state; |
117 | object->event_mask = | 183 | restart_masked: |
118 | FSCACHE_OBJECT_EVENTS_MASK & | 184 | events = object->events; |
119 | ~(1 << FSCACHE_OBJECT_EV_CLEARED); | 185 | |
120 | fscache_initialise_object(object); | 186 | /* Handle any out-of-band events (typically an error) */ |
121 | goto done; | 187 | if (events & object->oob_event_mask) { |
122 | 188 | _debug("{OBJ%x} oob %lx", | |
123 | /* look up the object metadata on disk */ | 189 | object->debug_id, events & object->oob_event_mask); |
124 | case FSCACHE_OBJECT_LOOKING_UP: | 190 | for (t = object->oob_table; t->events; t++) { |
125 | fscache_lookup_object(object); | 191 | if (events & t->events) { |
126 | goto lookup_transit; | 192 | state = t->transit_to; |
127 | 193 | ASSERT(state->work != NULL); | |
128 | /* create the object metadata on disk */ | 194 | event = fls(events & t->events) - 1; |
129 | case FSCACHE_OBJECT_CREATING: | 195 | __clear_bit(event, &object->oob_event_mask); |
130 | fscache_lookup_object(object); | 196 | clear_bit(event, &object->events); |
131 | goto lookup_transit; | 197 | goto execute_work_state; |
132 | 198 | } | |
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 | } | 199 | } |
182 | spin_unlock(&object->lock); | 200 | } |
183 | 201 | ||
184 | fscache_done_parent_op(object); | 202 | /* Wait states are just transition tables */ |
203 | if (!state->work) { | ||
204 | if (events & event_mask) { | ||
205 | for (t = state->transitions; t->events; t++) { | ||
206 | if (events & t->events) { | ||
207 | new_state = t->transit_to; | ||
208 | event = fls(events & t->events) - 1; | ||
209 | clear_bit(event, &object->events); | ||
210 | _debug("{OBJ%x} ev %d: %s -> %s", | ||
211 | object->debug_id, event, | ||
212 | state->name, new_state->name); | ||
213 | object->state = state = new_state; | ||
214 | goto execute_work_state; | ||
215 | } | ||
216 | } | ||
185 | 217 | ||
186 | /* wait for completion of all active operations on this object | 218 | /* The event mask didn't include all the tabled bits */ |
187 | * and the death of all child objects of this object */ | 219 | 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 | } | 220 | } |
211 | spin_unlock(&object->lock); | 221 | /* Randomly woke up */ |
212 | fscache_enqueue_dependents(object); | 222 | 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 | } | ||
276 | |||
277 | /* determine the transition from a lookup state */ | ||
278 | lookup_transit: | ||
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 | } | 223 | } |
297 | 224 | ||
298 | /* determine the transition from an active state */ | 225 | execute_work_state: |
299 | active_transit: | 226 | _debug("{OBJ%x} exec %s", object->debug_id, state->name); |
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 | 227 | ||
321 | /* determine the transition from a terminal state */ | 228 | new_state = state->work(object, event); |
322 | terminal_transit: | 229 | event = -1; |
323 | event = fls(object->events & object->event_mask) - 1; | 230 | if (new_state == NO_TRANSIT) { |
324 | switch (event) { | 231 | _debug("{OBJ%x} %s notrans", object->debug_id, state->name); |
325 | case FSCACHE_OBJECT_EV_WITHDRAW: | 232 | fscache_enqueue_object(object); |
326 | new_state = FSCACHE_OBJECT_WITHDRAWING; | 233 | event_mask = object->oob_event_mask; |
327 | goto change_state; | 234 | 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 | } | 235 | } |
345 | 236 | ||
346 | change_state: | 237 | _debug("{OBJ%x} %s -> %s", |
347 | spin_lock(&object->lock); | 238 | object->debug_id, state->name, new_state->name); |
348 | object->state = new_state; | 239 | object->state = state = new_state; |
349 | spin_unlock(&object->lock); | ||
350 | 240 | ||
351 | done: | 241 | if (state->work) { |
352 | _leave(" [->%s]", fscache_object_states[object->state]); | 242 | if (unlikely(state->work == ((void *)2UL))) { |
353 | return; | 243 | _leave(" [dead]"); |
244 | return; | ||
245 | } | ||
246 | goto restart_masked; | ||
247 | } | ||
354 | 248 | ||
355 | unsupported_event: | 249 | /* Transited to wait state */ |
356 | printk(KERN_ERR "FS-Cache:" | 250 | event_mask = object->oob_event_mask; |
357 | " Unsupported event %d [%lx/%lx] in state %s\n", | 251 | for (t = state->transitions; t->events; t++) |
358 | event, object->events, object->event_mask, | 252 | event_mask |= t->events; |
359 | fscache_object_states[object->state]); | 253 | |
360 | BUG(); | 254 | unmask_events: |
255 | object->event_mask = event_mask; | ||
256 | smp_mb(); | ||
257 | events = object->events; | ||
258 | if (events & event_mask) | ||
259 | goto restart; | ||
260 | _leave(" [msk %lx]", event_mask); | ||
361 | } | 261 | } |
362 | 262 | ||
363 | /* | 263 | /* |
364 | * execute an object | 264 | * execute an object |
365 | */ | 265 | */ |
366 | void fscache_object_work_func(struct work_struct *work) | 266 | static void fscache_object_work_func(struct work_struct *work) |
367 | { | 267 | { |
368 | struct fscache_object *object = | 268 | struct fscache_object *object = |
369 | container_of(work, struct fscache_object, work); | 269 | container_of(work, struct fscache_object, work); |
@@ -372,14 +272,70 @@ void fscache_object_work_func(struct work_struct *work) | |||
372 | _enter("{OBJ%x}", object->debug_id); | 272 | _enter("{OBJ%x}", object->debug_id); |
373 | 273 | ||
374 | start = jiffies; | 274 | start = jiffies; |
375 | fscache_object_state_machine(object); | 275 | fscache_object_sm_dispatcher(object); |
376 | fscache_hist(fscache_objs_histogram, start); | 276 | 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); | 277 | fscache_put_object(object); |
381 | } | 278 | } |
382 | EXPORT_SYMBOL(fscache_object_work_func); | 279 | |
280 | /** | ||
281 | * fscache_object_init - Initialise a cache object description | ||
282 | * @object: Object description | ||
283 | * @cookie: Cookie object will be attached to | ||
284 | * @cache: Cache in which backing object will be found | ||
285 | * | ||
286 | * Initialise a cache object description to its basic values. | ||
287 | * | ||
288 | * See Documentation/filesystems/caching/backend-api.txt for a complete | ||
289 | * description. | ||
290 | */ | ||
291 | void fscache_object_init(struct fscache_object *object, | ||
292 | struct fscache_cookie *cookie, | ||
293 | struct fscache_cache *cache) | ||
294 | { | ||
295 | const struct fscache_transition *t; | ||
296 | |||
297 | atomic_inc(&cache->object_count); | ||
298 | |||
299 | object->state = STATE(WAIT_FOR_INIT); | ||
300 | object->oob_table = fscache_osm_init_oob; | ||
301 | object->flags = 1 << FSCACHE_OBJECT_IS_LIVE; | ||
302 | spin_lock_init(&object->lock); | ||
303 | INIT_LIST_HEAD(&object->cache_link); | ||
304 | INIT_HLIST_NODE(&object->cookie_link); | ||
305 | INIT_WORK(&object->work, fscache_object_work_func); | ||
306 | INIT_LIST_HEAD(&object->dependents); | ||
307 | INIT_LIST_HEAD(&object->dep_link); | ||
308 | INIT_LIST_HEAD(&object->pending_ops); | ||
309 | object->n_children = 0; | ||
310 | object->n_ops = object->n_in_progress = object->n_exclusive = 0; | ||
311 | object->events = 0; | ||
312 | object->store_limit = 0; | ||
313 | object->store_limit_l = 0; | ||
314 | object->cache = cache; | ||
315 | object->cookie = cookie; | ||
316 | object->parent = NULL; | ||
317 | |||
318 | object->oob_event_mask = 0; | ||
319 | for (t = object->oob_table; t->events; t++) | ||
320 | object->oob_event_mask |= t->events; | ||
321 | object->event_mask = object->oob_event_mask; | ||
322 | for (t = object->state->transitions; t->events; t++) | ||
323 | object->event_mask |= t->events; | ||
324 | } | ||
325 | EXPORT_SYMBOL(fscache_object_init); | ||
326 | |||
327 | /* | ||
328 | * Abort object initialisation before we start it. | ||
329 | */ | ||
330 | static const struct fscache_state *fscache_abort_initialisation(struct fscache_object *object, | ||
331 | int event) | ||
332 | { | ||
333 | _enter("{OBJ%x},%d", object->debug_id, event); | ||
334 | |||
335 | object->oob_event_mask = 0; | ||
336 | fscache_dequeue_object(object); | ||
337 | return transit_to(KILL_OBJECT); | ||
338 | } | ||
383 | 339 | ||
384 | /* | 340 | /* |
385 | * initialise an object | 341 | * initialise an object |
@@ -387,130 +343,136 @@ EXPORT_SYMBOL(fscache_object_work_func); | |||
387 | * immediately to do a creation | 343 | * immediately to do a creation |
388 | * - we may need to start the process of creating a parent and we need to wait | 344 | * - we may need to start the process of creating a parent and we need to wait |
389 | * for the parent's lookup and creation to complete if it's not there yet | 345 | * for the parent's lookup and creation to complete if it's not there yet |
390 | * - an object's cookie is pinned until we clear FSCACHE_COOKIE_CREATING on the | ||
391 | * leaf-most cookies of the object and all its children | ||
392 | */ | 346 | */ |
393 | static void fscache_initialise_object(struct fscache_object *object) | 347 | static const struct fscache_state *fscache_initialise_object(struct fscache_object *object, |
348 | int event) | ||
394 | { | 349 | { |
395 | struct fscache_object *parent; | 350 | struct fscache_object *parent; |
351 | bool success; | ||
396 | 352 | ||
397 | _enter(""); | 353 | _enter("{OBJ%x},%d", object->debug_id, event); |
398 | ASSERT(object->cookie != NULL); | ||
399 | ASSERT(object->cookie->parent != NULL); | ||
400 | |||
401 | if (object->events & ((1 << FSCACHE_OBJECT_EV_ERROR) | | ||
402 | (1 << FSCACHE_OBJECT_EV_RELEASE) | | ||
403 | (1 << FSCACHE_OBJECT_EV_RETIRE) | | ||
404 | (1 << FSCACHE_OBJECT_EV_WITHDRAW))) { | ||
405 | _debug("abort init %lx", object->events); | ||
406 | spin_lock(&object->lock); | ||
407 | object->state = FSCACHE_OBJECT_ABORT_INIT; | ||
408 | spin_unlock(&object->lock); | ||
409 | return; | ||
410 | } | ||
411 | 354 | ||
412 | spin_lock(&object->cookie->lock); | 355 | ASSERT(list_empty(&object->dep_link)); |
413 | spin_lock_nested(&object->cookie->parent->lock, 1); | ||
414 | 356 | ||
415 | parent = object->parent; | 357 | parent = object->parent; |
416 | if (!parent) { | 358 | if (!parent) { |
417 | _debug("no parent"); | 359 | _leave(" [no parent]"); |
418 | set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); | 360 | return transit_to(DROP_OBJECT); |
419 | } else { | 361 | } |
420 | spin_lock(&object->lock); | ||
421 | spin_lock_nested(&parent->lock, 1); | ||
422 | _debug("parent %s", fscache_object_states[parent->state]); | ||
423 | |||
424 | if (parent->state >= FSCACHE_OBJECT_DYING) { | ||
425 | _debug("bad parent"); | ||
426 | set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); | ||
427 | } else if (parent->state < FSCACHE_OBJECT_AVAILABLE) { | ||
428 | _debug("wait"); | ||
429 | |||
430 | /* we may get woken up in this state by child objects | ||
431 | * binding on to us, so we need to make sure we don't | ||
432 | * add ourself to the list multiple times */ | ||
433 | if (list_empty(&object->dep_link)) { | ||
434 | fscache_stat(&fscache_n_cop_grab_object); | ||
435 | object->cache->ops->grab_object(object); | ||
436 | fscache_stat_d(&fscache_n_cop_grab_object); | ||
437 | list_add(&object->dep_link, | ||
438 | &parent->dependents); | ||
439 | |||
440 | /* fscache_acquire_non_index_cookie() uses this | ||
441 | * to wake the chain up */ | ||
442 | if (parent->state == FSCACHE_OBJECT_INIT) | ||
443 | fscache_enqueue_object(parent); | ||
444 | } | ||
445 | } else { | ||
446 | _debug("go"); | ||
447 | parent->n_ops++; | ||
448 | parent->n_obj_ops++; | ||
449 | object->lookup_jif = jiffies; | ||
450 | object->state = FSCACHE_OBJECT_LOOKING_UP; | ||
451 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
452 | } | ||
453 | 362 | ||
454 | spin_unlock(&parent->lock); | 363 | _debug("parent: %s of:%lx", parent->state->name, parent->flags); |
455 | spin_unlock(&object->lock); | 364 | |
365 | if (fscache_object_is_dying(parent)) { | ||
366 | _leave(" [bad parent]"); | ||
367 | return transit_to(DROP_OBJECT); | ||
456 | } | 368 | } |
457 | 369 | ||
458 | spin_unlock(&object->cookie->parent->lock); | 370 | if (fscache_object_is_available(parent)) { |
459 | spin_unlock(&object->cookie->lock); | 371 | _leave(" [ready]"); |
372 | return transit_to(PARENT_READY); | ||
373 | } | ||
374 | |||
375 | _debug("wait"); | ||
376 | |||
377 | spin_lock(&parent->lock); | ||
378 | fscache_stat(&fscache_n_cop_grab_object); | ||
379 | success = false; | ||
380 | if (fscache_object_is_live(parent) && | ||
381 | object->cache->ops->grab_object(object)) { | ||
382 | list_add(&object->dep_link, &parent->dependents); | ||
383 | success = true; | ||
384 | } | ||
385 | fscache_stat_d(&fscache_n_cop_grab_object); | ||
386 | spin_unlock(&parent->lock); | ||
387 | if (!success) { | ||
388 | _leave(" [grab failed]"); | ||
389 | return transit_to(DROP_OBJECT); | ||
390 | } | ||
391 | |||
392 | /* fscache_acquire_non_index_cookie() uses this | ||
393 | * to wake the chain up */ | ||
394 | fscache_raise_event(parent, FSCACHE_OBJECT_EV_NEW_CHILD); | ||
395 | _leave(" [wait]"); | ||
396 | return transit_to(WAIT_FOR_PARENT); | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * Once the parent object is ready, we should kick off our lookup op. | ||
401 | */ | ||
402 | static const struct fscache_state *fscache_parent_ready(struct fscache_object *object, | ||
403 | int event) | ||
404 | { | ||
405 | struct fscache_object *parent = object->parent; | ||
406 | |||
407 | _enter("{OBJ%x},%d", object->debug_id, event); | ||
408 | |||
409 | ASSERT(parent != NULL); | ||
410 | |||
411 | spin_lock(&parent->lock); | ||
412 | parent->n_ops++; | ||
413 | parent->n_obj_ops++; | ||
414 | object->lookup_jif = jiffies; | ||
415 | spin_unlock(&parent->lock); | ||
416 | |||
460 | _leave(""); | 417 | _leave(""); |
418 | return transit_to(LOOK_UP_OBJECT); | ||
461 | } | 419 | } |
462 | 420 | ||
463 | /* | 421 | /* |
464 | * look an object up in the cache from which it was allocated | 422 | * look an object up in the cache from which it was allocated |
465 | * - we hold an "access lock" on the parent object, so the parent object cannot | 423 | * - we hold an "access lock" on the parent object, so the parent object cannot |
466 | * be withdrawn by either party till we've finished | 424 | * be withdrawn by either party till we've finished |
467 | * - an object's cookie is pinned until we clear FSCACHE_COOKIE_CREATING on the | ||
468 | * leaf-most cookies of the object and all its children | ||
469 | */ | 425 | */ |
470 | static void fscache_lookup_object(struct fscache_object *object) | 426 | static const struct fscache_state *fscache_look_up_object(struct fscache_object *object, |
427 | int event) | ||
471 | { | 428 | { |
472 | struct fscache_cookie *cookie = object->cookie; | 429 | struct fscache_cookie *cookie = object->cookie; |
473 | struct fscache_object *parent; | 430 | struct fscache_object *parent = object->parent; |
474 | int ret; | 431 | int ret; |
475 | 432 | ||
476 | _enter(""); | 433 | _enter("{OBJ%x},%d", object->debug_id, event); |
434 | |||
435 | object->oob_table = fscache_osm_lookup_oob; | ||
477 | 436 | ||
478 | parent = object->parent; | ||
479 | ASSERT(parent != NULL); | 437 | ASSERT(parent != NULL); |
480 | ASSERTCMP(parent->n_ops, >, 0); | 438 | ASSERTCMP(parent->n_ops, >, 0); |
481 | ASSERTCMP(parent->n_obj_ops, >, 0); | 439 | ASSERTCMP(parent->n_obj_ops, >, 0); |
482 | 440 | ||
483 | /* make sure the parent is still available */ | 441 | /* make sure the parent is still available */ |
484 | ASSERTCMP(parent->state, >=, FSCACHE_OBJECT_AVAILABLE); | 442 | ASSERT(fscache_object_is_available(parent)); |
485 | 443 | ||
486 | if (parent->state >= FSCACHE_OBJECT_DYING || | 444 | if (fscache_object_is_dying(parent) || |
487 | test_bit(FSCACHE_IOERROR, &object->cache->flags)) { | 445 | test_bit(FSCACHE_IOERROR, &object->cache->flags) || |
488 | _debug("unavailable"); | 446 | !fscache_use_cookie(object)) { |
489 | set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); | 447 | _leave(" [unavailable]"); |
490 | _leave(""); | 448 | return transit_to(LOOKUP_FAILURE); |
491 | return; | ||
492 | } | 449 | } |
493 | 450 | ||
494 | _debug("LOOKUP \"%s/%s\" in \"%s\"", | 451 | _debug("LOOKUP \"%s\" in \"%s\"", |
495 | parent->cookie->def->name, cookie->def->name, | 452 | cookie->def->name, object->cache->tag->name); |
496 | object->cache->tag->name); | ||
497 | 453 | ||
498 | fscache_stat(&fscache_n_object_lookups); | 454 | fscache_stat(&fscache_n_object_lookups); |
499 | fscache_stat(&fscache_n_cop_lookup_object); | 455 | fscache_stat(&fscache_n_cop_lookup_object); |
500 | ret = object->cache->ops->lookup_object(object); | 456 | ret = object->cache->ops->lookup_object(object); |
501 | fscache_stat_d(&fscache_n_cop_lookup_object); | 457 | fscache_stat_d(&fscache_n_cop_lookup_object); |
502 | 458 | ||
503 | if (test_bit(FSCACHE_OBJECT_EV_ERROR, &object->events)) | 459 | fscache_unuse_cookie(object); |
504 | set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); | ||
505 | 460 | ||
506 | if (ret == -ETIMEDOUT) { | 461 | if (ret == -ETIMEDOUT) { |
507 | /* probably stuck behind another object, so move this one to | 462 | /* probably stuck behind another object, so move this one to |
508 | * the back of the queue */ | 463 | * the back of the queue */ |
509 | fscache_stat(&fscache_n_object_lookups_timed_out); | 464 | fscache_stat(&fscache_n_object_lookups_timed_out); |
510 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | 465 | _leave(" [timeout]"); |
466 | return NO_TRANSIT; | ||
511 | } | 467 | } |
512 | 468 | ||
513 | _leave(""); | 469 | if (ret < 0) { |
470 | _leave(" [error]"); | ||
471 | return transit_to(LOOKUP_FAILURE); | ||
472 | } | ||
473 | |||
474 | _leave(" [ok]"); | ||
475 | return transit_to(OBJECT_AVAILABLE); | ||
514 | } | 476 | } |
515 | 477 | ||
516 | /** | 478 | /** |
@@ -524,32 +486,20 @@ void fscache_object_lookup_negative(struct fscache_object *object) | |||
524 | { | 486 | { |
525 | struct fscache_cookie *cookie = object->cookie; | 487 | struct fscache_cookie *cookie = object->cookie; |
526 | 488 | ||
527 | _enter("{OBJ%x,%s}", | 489 | _enter("{OBJ%x,%s}", object->debug_id, object->state->name); |
528 | object->debug_id, fscache_object_states[object->state]); | ||
529 | 490 | ||
530 | spin_lock(&object->lock); | 491 | if (!test_and_set_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) { |
531 | if (object->state == FSCACHE_OBJECT_LOOKING_UP) { | ||
532 | fscache_stat(&fscache_n_object_lookups_negative); | 492 | fscache_stat(&fscache_n_object_lookups_negative); |
533 | 493 | ||
534 | /* transit here to allow write requests to begin stacking up | 494 | /* Allow write requests to begin stacking up and read requests to begin |
535 | * and read requests to begin returning ENODATA */ | 495 | * returning ENODATA. |
536 | object->state = FSCACHE_OBJECT_CREATING; | 496 | */ |
537 | spin_unlock(&object->lock); | ||
538 | |||
539 | set_bit(FSCACHE_COOKIE_PENDING_FILL, &cookie->flags); | ||
540 | set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); | 497 | set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); |
541 | 498 | ||
542 | _debug("wake up lookup %p", &cookie->flags); | 499 | _debug("wake up lookup %p", &cookie->flags); |
543 | smp_mb__before_clear_bit(); | 500 | clear_bit_unlock(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); |
544 | clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); | ||
545 | smp_mb__after_clear_bit(); | ||
546 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); | 501 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); |
547 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
548 | } else { | ||
549 | ASSERTCMP(object->state, ==, FSCACHE_OBJECT_CREATING); | ||
550 | spin_unlock(&object->lock); | ||
551 | } | 502 | } |
552 | |||
553 | _leave(""); | 503 | _leave(""); |
554 | } | 504 | } |
555 | EXPORT_SYMBOL(fscache_object_lookup_negative); | 505 | EXPORT_SYMBOL(fscache_object_lookup_negative); |
@@ -568,38 +518,26 @@ void fscache_obtained_object(struct fscache_object *object) | |||
568 | { | 518 | { |
569 | struct fscache_cookie *cookie = object->cookie; | 519 | struct fscache_cookie *cookie = object->cookie; |
570 | 520 | ||
571 | _enter("{OBJ%x,%s}", | 521 | _enter("{OBJ%x,%s}", object->debug_id, object->state->name); |
572 | object->debug_id, fscache_object_states[object->state]); | ||
573 | 522 | ||
574 | /* if we were still looking up, then we must have a positive lookup | 523 | /* if we were still looking up, then we must have a positive lookup |
575 | * result, in which case there may be data available */ | 524 | * result, in which case there may be data available */ |
576 | spin_lock(&object->lock); | 525 | if (!test_and_set_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) { |
577 | if (object->state == FSCACHE_OBJECT_LOOKING_UP) { | ||
578 | fscache_stat(&fscache_n_object_lookups_positive); | 526 | fscache_stat(&fscache_n_object_lookups_positive); |
579 | 527 | ||
580 | clear_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); | 528 | /* We do (presumably) have data */ |
529 | clear_bit_unlock(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); | ||
581 | 530 | ||
582 | object->state = FSCACHE_OBJECT_AVAILABLE; | 531 | /* Allow write requests to begin stacking up and read requests |
583 | spin_unlock(&object->lock); | 532 | * to begin shovelling data. |
584 | 533 | */ | |
585 | smp_mb__before_clear_bit(); | 534 | clear_bit_unlock(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); |
586 | clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); | ||
587 | smp_mb__after_clear_bit(); | ||
588 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); | 535 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); |
589 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
590 | } else { | 536 | } else { |
591 | ASSERTCMP(object->state, ==, FSCACHE_OBJECT_CREATING); | ||
592 | fscache_stat(&fscache_n_object_created); | 537 | fscache_stat(&fscache_n_object_created); |
593 | |||
594 | object->state = FSCACHE_OBJECT_AVAILABLE; | ||
595 | spin_unlock(&object->lock); | ||
596 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
597 | smp_wmb(); | ||
598 | } | 538 | } |
599 | 539 | ||
600 | if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, &cookie->flags)) | 540 | set_bit(FSCACHE_OBJECT_IS_AVAILABLE, &object->flags); |
601 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_CREATING); | ||
602 | |||
603 | _leave(""); | 541 | _leave(""); |
604 | } | 542 | } |
605 | EXPORT_SYMBOL(fscache_obtained_object); | 543 | EXPORT_SYMBOL(fscache_obtained_object); |
@@ -607,15 +545,14 @@ EXPORT_SYMBOL(fscache_obtained_object); | |||
607 | /* | 545 | /* |
608 | * handle an object that has just become available | 546 | * handle an object that has just become available |
609 | */ | 547 | */ |
610 | static void fscache_object_available(struct fscache_object *object) | 548 | static const struct fscache_state *fscache_object_available(struct fscache_object *object, |
549 | int event) | ||
611 | { | 550 | { |
612 | _enter("{OBJ%x}", object->debug_id); | 551 | _enter("{OBJ%x},%d", object->debug_id, event); |
613 | 552 | ||
614 | spin_lock(&object->lock); | 553 | object->oob_table = fscache_osm_run_oob; |
615 | 554 | ||
616 | if (object->cookie && | 555 | spin_lock(&object->lock); |
617 | test_and_clear_bit(FSCACHE_COOKIE_CREATING, &object->cookie->flags)) | ||
618 | wake_up_bit(&object->cookie->flags, FSCACHE_COOKIE_CREATING); | ||
619 | 556 | ||
620 | fscache_done_parent_op(object); | 557 | fscache_done_parent_op(object); |
621 | if (object->n_in_progress == 0) { | 558 | if (object->n_in_progress == 0) { |
@@ -631,130 +568,158 @@ static void fscache_object_available(struct fscache_object *object) | |||
631 | fscache_stat(&fscache_n_cop_lookup_complete); | 568 | fscache_stat(&fscache_n_cop_lookup_complete); |
632 | object->cache->ops->lookup_complete(object); | 569 | object->cache->ops->lookup_complete(object); |
633 | fscache_stat_d(&fscache_n_cop_lookup_complete); | 570 | fscache_stat_d(&fscache_n_cop_lookup_complete); |
634 | fscache_enqueue_dependents(object); | ||
635 | 571 | ||
636 | fscache_hist(fscache_obj_instantiate_histogram, object->lookup_jif); | 572 | fscache_hist(fscache_obj_instantiate_histogram, object->lookup_jif); |
637 | fscache_stat(&fscache_n_object_avail); | 573 | fscache_stat(&fscache_n_object_avail); |
638 | 574 | ||
639 | _leave(""); | 575 | _leave(""); |
576 | return transit_to(JUMPSTART_DEPS); | ||
640 | } | 577 | } |
641 | 578 | ||
642 | /* | 579 | /* |
643 | * drop an object's attachments | 580 | * Wake up this object's dependent objects now that we've become available. |
644 | */ | 581 | */ |
645 | static void fscache_drop_object(struct fscache_object *object) | 582 | static const struct fscache_state *fscache_jumpstart_dependents(struct fscache_object *object, |
583 | int event) | ||
646 | { | 584 | { |
647 | struct fscache_object *parent = object->parent; | 585 | _enter("{OBJ%x},%d", object->debug_id, event); |
648 | struct fscache_cache *cache = object->cache; | ||
649 | 586 | ||
650 | _enter("{OBJ%x,%d}", object->debug_id, object->n_children); | 587 | if (!fscache_enqueue_dependents(object, FSCACHE_OBJECT_EV_PARENT_READY)) |
588 | return NO_TRANSIT; /* Not finished; requeue */ | ||
589 | return transit_to(WAIT_FOR_CMD); | ||
590 | } | ||
651 | 591 | ||
652 | ASSERTCMP(object->cookie, ==, NULL); | 592 | /* |
653 | ASSERT(hlist_unhashed(&object->cookie_link)); | 593 | * Handle lookup or creation failute. |
594 | */ | ||
595 | static const struct fscache_state *fscache_lookup_failure(struct fscache_object *object, | ||
596 | int event) | ||
597 | { | ||
598 | struct fscache_cookie *cookie; | ||
654 | 599 | ||
655 | spin_lock(&cache->object_list_lock); | 600 | _enter("{OBJ%x},%d", object->debug_id, event); |
656 | list_del_init(&object->cache_link); | ||
657 | spin_unlock(&cache->object_list_lock); | ||
658 | 601 | ||
659 | fscache_stat(&fscache_n_cop_drop_object); | 602 | object->oob_event_mask = 0; |
660 | cache->ops->drop_object(object); | ||
661 | fscache_stat_d(&fscache_n_cop_drop_object); | ||
662 | 603 | ||
663 | if (parent) { | 604 | fscache_stat(&fscache_n_cop_lookup_complete); |
664 | _debug("release parent OBJ%x {%d}", | 605 | object->cache->ops->lookup_complete(object); |
665 | parent->debug_id, parent->n_children); | 606 | fscache_stat_d(&fscache_n_cop_lookup_complete); |
666 | 607 | ||
667 | spin_lock(&parent->lock); | 608 | cookie = object->cookie; |
668 | parent->n_children--; | 609 | set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); |
669 | if (parent->n_children == 0) | 610 | if (test_and_clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags)) |
670 | fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); | 611 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); |
671 | spin_unlock(&parent->lock); | 612 | |
672 | object->parent = NULL; | 613 | fscache_done_parent_op(object); |
614 | return transit_to(KILL_OBJECT); | ||
615 | } | ||
616 | |||
617 | /* | ||
618 | * Wait for completion of all active operations on this object and the death of | ||
619 | * all child objects of this object. | ||
620 | */ | ||
621 | static const struct fscache_state *fscache_kill_object(struct fscache_object *object, | ||
622 | int event) | ||
623 | { | ||
624 | _enter("{OBJ%x,%d,%d},%d", | ||
625 | object->debug_id, object->n_ops, object->n_children, event); | ||
626 | |||
627 | clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); | ||
628 | object->oob_event_mask = 0; | ||
629 | |||
630 | if (list_empty(&object->dependents) && | ||
631 | object->n_ops == 0 && | ||
632 | object->n_children == 0) | ||
633 | return transit_to(DROP_OBJECT); | ||
634 | |||
635 | if (object->n_in_progress == 0) { | ||
636 | spin_lock(&object->lock); | ||
637 | if (object->n_ops > 0 && object->n_in_progress == 0) | ||
638 | fscache_start_operations(object); | ||
639 | spin_unlock(&object->lock); | ||
673 | } | 640 | } |
674 | 641 | ||
675 | /* this just shifts the object release to the work processor */ | 642 | if (!list_empty(&object->dependents)) |
676 | fscache_put_object(object); | 643 | return transit_to(KILL_DEPENDENTS); |
677 | 644 | ||
678 | _leave(""); | 645 | return transit_to(WAIT_FOR_CLEARANCE); |
679 | } | 646 | } |
680 | 647 | ||
681 | /* | 648 | /* |
682 | * release or recycle an object that the netfs has discarded | 649 | * Kill dependent objects. |
683 | */ | 650 | */ |
684 | static void fscache_release_object(struct fscache_object *object) | 651 | static const struct fscache_state *fscache_kill_dependents(struct fscache_object *object, |
652 | int event) | ||
685 | { | 653 | { |
686 | _enter(""); | 654 | _enter("{OBJ%x},%d", object->debug_id, event); |
687 | 655 | ||
688 | fscache_drop_object(object); | 656 | if (!fscache_enqueue_dependents(object, FSCACHE_OBJECT_EV_KILL)) |
657 | return NO_TRANSIT; /* Not finished */ | ||
658 | return transit_to(WAIT_FOR_CLEARANCE); | ||
689 | } | 659 | } |
690 | 660 | ||
691 | /* | 661 | /* |
692 | * withdraw an object from active service | 662 | * Drop an object's attachments |
693 | */ | 663 | */ |
694 | static void fscache_withdraw_object(struct fscache_object *object) | 664 | static const struct fscache_state *fscache_drop_object(struct fscache_object *object, |
665 | int event) | ||
695 | { | 666 | { |
696 | struct fscache_cookie *cookie; | 667 | struct fscache_object *parent = object->parent; |
697 | bool detached; | 668 | struct fscache_cookie *cookie = object->cookie; |
669 | struct fscache_cache *cache = object->cache; | ||
670 | bool awaken = false; | ||
698 | 671 | ||
699 | _enter(""); | 672 | _enter("{OBJ%x,%d},%d", object->debug_id, object->n_children, event); |
700 | 673 | ||
701 | spin_lock(&object->lock); | 674 | ASSERT(cookie != NULL); |
702 | cookie = object->cookie; | 675 | ASSERT(!hlist_unhashed(&object->cookie_link)); |
703 | if (cookie) { | ||
704 | /* need to get the cookie lock before the object lock, starting | ||
705 | * from the object pointer */ | ||
706 | atomic_inc(&cookie->usage); | ||
707 | spin_unlock(&object->lock); | ||
708 | 676 | ||
709 | detached = false; | 677 | /* Make sure the cookie no longer points here and that the netfs isn't |
710 | spin_lock(&cookie->lock); | 678 | * waiting for us. |
711 | spin_lock(&object->lock); | 679 | */ |
680 | spin_lock(&cookie->lock); | ||
681 | hlist_del_init(&object->cookie_link); | ||
682 | if (test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) | ||
683 | awaken = true; | ||
684 | spin_unlock(&cookie->lock); | ||
712 | 685 | ||
713 | if (object->cookie == cookie) { | 686 | if (awaken) |
714 | hlist_del_init(&object->cookie_link); | 687 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); |
715 | object->cookie = NULL; | ||
716 | fscache_invalidation_complete(cookie); | ||
717 | detached = true; | ||
718 | } | ||
719 | spin_unlock(&cookie->lock); | ||
720 | fscache_cookie_put(cookie); | ||
721 | if (detached) | ||
722 | fscache_cookie_put(cookie); | ||
723 | } | ||
724 | 688 | ||
689 | /* Prevent a race with our last child, which has to signal EV_CLEARED | ||
690 | * before dropping our spinlock. | ||
691 | */ | ||
692 | spin_lock(&object->lock); | ||
725 | spin_unlock(&object->lock); | 693 | spin_unlock(&object->lock); |
726 | 694 | ||
727 | fscache_drop_object(object); | 695 | /* Discard from the cache's collection of objects */ |
728 | } | 696 | spin_lock(&cache->object_list_lock); |
697 | list_del_init(&object->cache_link); | ||
698 | spin_unlock(&cache->object_list_lock); | ||
729 | 699 | ||
730 | /* | 700 | fscache_stat(&fscache_n_cop_drop_object); |
731 | * withdraw an object from active service at the behest of the cache | 701 | cache->ops->drop_object(object); |
732 | * - need break the links to a cached object cookie | 702 | fscache_stat_d(&fscache_n_cop_drop_object); |
733 | * - called under two situations: | ||
734 | * (1) recycler decides to reclaim an in-use object | ||
735 | * (2) a cache is unmounted | ||
736 | * - have to take care as the cookie can be being relinquished by the netfs | ||
737 | * simultaneously | ||
738 | * - the object is pinned by the caller holding a refcount on it | ||
739 | */ | ||
740 | void fscache_withdrawing_object(struct fscache_cache *cache, | ||
741 | struct fscache_object *object) | ||
742 | { | ||
743 | bool enqueue = false; | ||
744 | 703 | ||
745 | _enter(",OBJ%x", object->debug_id); | 704 | /* The parent object wants to know when all it dependents have gone */ |
705 | if (parent) { | ||
706 | _debug("release parent OBJ%x {%d}", | ||
707 | parent->debug_id, parent->n_children); | ||
746 | 708 | ||
747 | spin_lock(&object->lock); | 709 | spin_lock(&parent->lock); |
748 | if (object->state < FSCACHE_OBJECT_WITHDRAWING) { | 710 | parent->n_children--; |
749 | object->state = FSCACHE_OBJECT_WITHDRAWING; | 711 | if (parent->n_children == 0) |
750 | enqueue = true; | 712 | fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); |
713 | spin_unlock(&parent->lock); | ||
714 | object->parent = NULL; | ||
751 | } | 715 | } |
752 | spin_unlock(&object->lock); | ||
753 | 716 | ||
754 | if (enqueue) | 717 | /* this just shifts the object release to the work processor */ |
755 | fscache_enqueue_object(object); | 718 | fscache_put_object(object); |
719 | fscache_stat(&fscache_n_object_dead); | ||
756 | 720 | ||
757 | _leave(""); | 721 | _leave(""); |
722 | return transit_to(OBJECT_DEAD); | ||
758 | } | 723 | } |
759 | 724 | ||
760 | /* | 725 | /* |
@@ -771,7 +736,7 @@ static int fscache_get_object(struct fscache_object *object) | |||
771 | } | 736 | } |
772 | 737 | ||
773 | /* | 738 | /* |
774 | * discard a ref on a work item | 739 | * Discard a ref on an object |
775 | */ | 740 | */ |
776 | static void fscache_put_object(struct fscache_object *object) | 741 | static void fscache_put_object(struct fscache_object *object) |
777 | { | 742 | { |
@@ -780,6 +745,22 @@ static void fscache_put_object(struct fscache_object *object) | |||
780 | fscache_stat_d(&fscache_n_cop_put_object); | 745 | fscache_stat_d(&fscache_n_cop_put_object); |
781 | } | 746 | } |
782 | 747 | ||
748 | /** | ||
749 | * fscache_object_destroy - Note that a cache object is about to be destroyed | ||
750 | * @object: The object to be destroyed | ||
751 | * | ||
752 | * Note the imminent destruction and deallocation of a cache object record. | ||
753 | */ | ||
754 | void fscache_object_destroy(struct fscache_object *object) | ||
755 | { | ||
756 | fscache_objlist_remove(object); | ||
757 | |||
758 | /* We can get rid of the cookie now */ | ||
759 | fscache_cookie_put(object->cookie); | ||
760 | object->cookie = NULL; | ||
761 | } | ||
762 | EXPORT_SYMBOL(fscache_object_destroy); | ||
763 | |||
783 | /* | 764 | /* |
784 | * enqueue an object for metadata-type processing | 765 | * enqueue an object for metadata-type processing |
785 | */ | 766 | */ |
@@ -803,7 +784,7 @@ void fscache_enqueue_object(struct fscache_object *object) | |||
803 | 784 | ||
804 | /** | 785 | /** |
805 | * fscache_object_sleep_till_congested - Sleep until object wq is congested | 786 | * fscache_object_sleep_till_congested - Sleep until object wq is congested |
806 | * @timoutp: Scheduler sleep timeout | 787 | * @timeoutp: Scheduler sleep timeout |
807 | * | 788 | * |
808 | * Allow an object handler to sleep until the object workqueue is congested. | 789 | * Allow an object handler to sleep until the object workqueue is congested. |
809 | * | 790 | * |
@@ -831,18 +812,21 @@ bool fscache_object_sleep_till_congested(signed long *timeoutp) | |||
831 | EXPORT_SYMBOL_GPL(fscache_object_sleep_till_congested); | 812 | EXPORT_SYMBOL_GPL(fscache_object_sleep_till_congested); |
832 | 813 | ||
833 | /* | 814 | /* |
834 | * enqueue the dependents of an object for metadata-type processing | 815 | * Enqueue the dependents of an object for metadata-type processing. |
835 | * - the caller must hold the object's lock | 816 | * |
836 | * - this may cause an already locked object to wind up being processed again | 817 | * If we don't manage to finish the list before the scheduler wants to run |
818 | * again then return false immediately. We return true if the list was | ||
819 | * cleared. | ||
837 | */ | 820 | */ |
838 | static void fscache_enqueue_dependents(struct fscache_object *object) | 821 | static bool fscache_enqueue_dependents(struct fscache_object *object, int event) |
839 | { | 822 | { |
840 | struct fscache_object *dep; | 823 | struct fscache_object *dep; |
824 | bool ret = true; | ||
841 | 825 | ||
842 | _enter("{OBJ%x}", object->debug_id); | 826 | _enter("{OBJ%x}", object->debug_id); |
843 | 827 | ||
844 | if (list_empty(&object->dependents)) | 828 | if (list_empty(&object->dependents)) |
845 | return; | 829 | return true; |
846 | 830 | ||
847 | spin_lock(&object->lock); | 831 | spin_lock(&object->lock); |
848 | 832 | ||
@@ -851,23 +835,23 @@ static void fscache_enqueue_dependents(struct fscache_object *object) | |||
851 | struct fscache_object, dep_link); | 835 | struct fscache_object, dep_link); |
852 | list_del_init(&dep->dep_link); | 836 | list_del_init(&dep->dep_link); |
853 | 837 | ||
854 | 838 | fscache_raise_event(dep, event); | |
855 | /* sort onto appropriate lists */ | ||
856 | fscache_enqueue_object(dep); | ||
857 | fscache_put_object(dep); | 839 | fscache_put_object(dep); |
858 | 840 | ||
859 | if (!list_empty(&object->dependents)) | 841 | if (!list_empty(&object->dependents) && need_resched()) { |
860 | cond_resched_lock(&object->lock); | 842 | ret = false; |
843 | break; | ||
844 | } | ||
861 | } | 845 | } |
862 | 846 | ||
863 | spin_unlock(&object->lock); | 847 | spin_unlock(&object->lock); |
848 | return ret; | ||
864 | } | 849 | } |
865 | 850 | ||
866 | /* | 851 | /* |
867 | * remove an object from whatever queue it's waiting on | 852 | * remove an object from whatever queue it's waiting on |
868 | * - the caller must hold object->lock | ||
869 | */ | 853 | */ |
870 | void fscache_dequeue_object(struct fscache_object *object) | 854 | static void fscache_dequeue_object(struct fscache_object *object) |
871 | { | 855 | { |
872 | _enter("{OBJ%x}", object->debug_id); | 856 | _enter("{OBJ%x}", object->debug_id); |
873 | 857 | ||
@@ -886,7 +870,10 @@ void fscache_dequeue_object(struct fscache_object *object) | |||
886 | * @data: The auxiliary data for the object | 870 | * @data: The auxiliary data for the object |
887 | * @datalen: The size of the auxiliary data | 871 | * @datalen: The size of the auxiliary data |
888 | * | 872 | * |
889 | * This function consults the netfs about the coherency state of an object | 873 | * This function consults the netfs about the coherency state of an object. |
874 | * The caller must be holding a ref on cookie->n_active (held by | ||
875 | * fscache_look_up_object() on behalf of the cache backend during object lookup | ||
876 | * and creation). | ||
890 | */ | 877 | */ |
891 | enum fscache_checkaux fscache_check_aux(struct fscache_object *object, | 878 | enum fscache_checkaux fscache_check_aux(struct fscache_object *object, |
892 | const void *data, uint16_t datalen) | 879 | const void *data, uint16_t datalen) |
@@ -927,12 +914,23 @@ EXPORT_SYMBOL(fscache_check_aux); | |||
927 | /* | 914 | /* |
928 | * Asynchronously invalidate an object. | 915 | * Asynchronously invalidate an object. |
929 | */ | 916 | */ |
930 | static void fscache_invalidate_object(struct fscache_object *object) | 917 | static const struct fscache_state *_fscache_invalidate_object(struct fscache_object *object, |
918 | int event) | ||
931 | { | 919 | { |
932 | struct fscache_operation *op; | 920 | struct fscache_operation *op; |
933 | struct fscache_cookie *cookie = object->cookie; | 921 | struct fscache_cookie *cookie = object->cookie; |
934 | 922 | ||
935 | _enter("{OBJ%x}", object->debug_id); | 923 | _enter("{OBJ%x},%d", object->debug_id, event); |
924 | |||
925 | /* We're going to need the cookie. If the cookie is not available then | ||
926 | * retire the object instead. | ||
927 | */ | ||
928 | if (!fscache_use_cookie(object)) { | ||
929 | ASSERT(object->cookie->stores.rnode == NULL); | ||
930 | set_bit(FSCACHE_COOKIE_RETIRED, &cookie->flags); | ||
931 | _leave(" [no cookie]"); | ||
932 | return transit_to(KILL_OBJECT); | ||
933 | } | ||
936 | 934 | ||
937 | /* Reject any new read/write ops and abort any that are pending. */ | 935 | /* Reject any new read/write ops and abort any that are pending. */ |
938 | fscache_invalidate_writes(cookie); | 936 | fscache_invalidate_writes(cookie); |
@@ -941,14 +939,13 @@ static void fscache_invalidate_object(struct fscache_object *object) | |||
941 | 939 | ||
942 | /* Now we have to wait for in-progress reads and writes */ | 940 | /* Now we have to wait for in-progress reads and writes */ |
943 | op = kzalloc(sizeof(*op), GFP_KERNEL); | 941 | op = kzalloc(sizeof(*op), GFP_KERNEL); |
944 | if (!op) { | 942 | if (!op) |
945 | fscache_raise_event(object, FSCACHE_OBJECT_EV_ERROR); | 943 | goto nomem; |
946 | _leave(" [ENOMEM]"); | ||
947 | return; | ||
948 | } | ||
949 | 944 | ||
950 | fscache_operation_init(op, object->cache->ops->invalidate_object, NULL); | 945 | fscache_operation_init(op, object->cache->ops->invalidate_object, NULL); |
951 | op->flags = FSCACHE_OP_ASYNC | (1 << FSCACHE_OP_EXCLUSIVE); | 946 | op->flags = FSCACHE_OP_ASYNC | |
947 | (1 << FSCACHE_OP_EXCLUSIVE) | | ||
948 | (1 << FSCACHE_OP_UNUSE_COOKIE); | ||
952 | 949 | ||
953 | spin_lock(&cookie->lock); | 950 | spin_lock(&cookie->lock); |
954 | if (fscache_submit_exclusive_op(object, op) < 0) | 951 | if (fscache_submit_exclusive_op(object, op) < 0) |
@@ -965,13 +962,50 @@ static void fscache_invalidate_object(struct fscache_object *object) | |||
965 | /* We can allow read and write requests to come in once again. They'll | 962 | /* We can allow read and write requests to come in once again. They'll |
966 | * queue up behind our exclusive invalidation operation. | 963 | * queue up behind our exclusive invalidation operation. |
967 | */ | 964 | */ |
968 | fscache_invalidation_complete(cookie); | 965 | if (test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) |
969 | _leave(""); | 966 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); |
970 | return; | 967 | _leave(" [ok]"); |
968 | return transit_to(UPDATE_OBJECT); | ||
969 | |||
970 | nomem: | ||
971 | clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); | ||
972 | fscache_unuse_cookie(object); | ||
973 | _leave(" [ENOMEM]"); | ||
974 | return transit_to(KILL_OBJECT); | ||
971 | 975 | ||
972 | submit_op_failed: | 976 | submit_op_failed: |
977 | clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); | ||
973 | spin_unlock(&cookie->lock); | 978 | spin_unlock(&cookie->lock); |
974 | kfree(op); | 979 | kfree(op); |
975 | fscache_raise_event(object, FSCACHE_OBJECT_EV_ERROR); | ||
976 | _leave(" [EIO]"); | 980 | _leave(" [EIO]"); |
981 | return transit_to(KILL_OBJECT); | ||
982 | } | ||
983 | |||
984 | static const struct fscache_state *fscache_invalidate_object(struct fscache_object *object, | ||
985 | int event) | ||
986 | { | ||
987 | const struct fscache_state *s; | ||
988 | |||
989 | fscache_stat(&fscache_n_invalidates_run); | ||
990 | fscache_stat(&fscache_n_cop_invalidate_object); | ||
991 | s = _fscache_invalidate_object(object, event); | ||
992 | fscache_stat_d(&fscache_n_cop_invalidate_object); | ||
993 | return s; | ||
994 | } | ||
995 | |||
996 | /* | ||
997 | * Asynchronously update an object. | ||
998 | */ | ||
999 | static const struct fscache_state *fscache_update_object(struct fscache_object *object, | ||
1000 | int event) | ||
1001 | { | ||
1002 | _enter("{OBJ%x},%d", object->debug_id, event); | ||
1003 | |||
1004 | fscache_stat(&fscache_n_updates_run); | ||
1005 | fscache_stat(&fscache_n_cop_update_object); | ||
1006 | object->cache->ops->update_object(object); | ||
1007 | fscache_stat_d(&fscache_n_cop_update_object); | ||
1008 | |||
1009 | _leave(""); | ||
1010 | return transit_to(WAIT_FOR_CMD); | ||
977 | } | 1011 | } |