diff options
-rw-r--r-- | Documentation/filesystems/caching/fscache.txt | 5 | ||||
-rw-r--r-- | Documentation/filesystems/caching/object.txt | 313 | ||||
-rw-r--r-- | fs/fscache/Makefile | 3 | ||||
-rw-r--r-- | fs/fscache/internal.h | 26 | ||||
-rw-r--r-- | fs/fscache/object.c | 810 |
5 files changed, 1155 insertions, 2 deletions
diff --git a/Documentation/filesystems/caching/fscache.txt b/Documentation/filesystems/caching/fscache.txt index 0a751f3c2c70..9e94b9491d89 100644 --- a/Documentation/filesystems/caching/fscache.txt +++ b/Documentation/filesystems/caching/fscache.txt | |||
@@ -188,6 +188,11 @@ The cache backend API to FS-Cache can be found in: | |||
188 | 188 | ||
189 | Documentation/filesystems/caching/backend-api.txt | 189 | Documentation/filesystems/caching/backend-api.txt |
190 | 190 | ||
191 | A description of the internal representations and object state machine can be | ||
192 | found in: | ||
193 | |||
194 | Documentation/filesystems/caching/object.txt | ||
195 | |||
191 | 196 | ||
192 | ======================= | 197 | ======================= |
193 | STATISTICAL INFORMATION | 198 | STATISTICAL INFORMATION |
diff --git a/Documentation/filesystems/caching/object.txt b/Documentation/filesystems/caching/object.txt new file mode 100644 index 000000000000..e8b0a35d8fe5 --- /dev/null +++ b/Documentation/filesystems/caching/object.txt | |||
@@ -0,0 +1,313 @@ | |||
1 | ==================================================== | ||
2 | IN-KERNEL CACHE OBJECT REPRESENTATION AND MANAGEMENT | ||
3 | ==================================================== | ||
4 | |||
5 | By: David Howells <dhowells@redhat.com> | ||
6 | |||
7 | Contents: | ||
8 | |||
9 | (*) Representation | ||
10 | |||
11 | (*) Object management state machine. | ||
12 | |||
13 | - Provision of cpu time. | ||
14 | - Locking simplification. | ||
15 | |||
16 | (*) The set of states. | ||
17 | |||
18 | (*) The set of events. | ||
19 | |||
20 | |||
21 | ============== | ||
22 | REPRESENTATION | ||
23 | ============== | ||
24 | |||
25 | FS-Cache maintains an in-kernel representation of each object that a netfs is | ||
26 | currently interested in. Such objects are represented by the fscache_cookie | ||
27 | struct and are referred to as cookies. | ||
28 | |||
29 | FS-Cache also maintains a separate in-kernel representation of the objects that | ||
30 | a cache backend is currently actively caching. Such objects are represented by | ||
31 | the fscache_object struct. The cache backends allocate these upon request, and | ||
32 | are expected to embed them in their own representations. These are referred to | ||
33 | as objects. | ||
34 | |||
35 | There is a 1:N relationship between cookies and objects. A cookie may be | ||
36 | represented by multiple objects - an index may exist in more than one cache - | ||
37 | or even by no objects (it may not be cached). | ||
38 | |||
39 | Furthermore, both cookies and objects are hierarchical. The two hierarchies | ||
40 | correspond, but the cookies tree is a superset of the union of the object trees | ||
41 | of multiple caches: | ||
42 | |||
43 | NETFS INDEX TREE : CACHE 1 : CACHE 2 | ||
44 | : : | ||
45 | : +-----------+ : | ||
46 | +----------->| IObject | : | ||
47 | +-----------+ | : +-----------+ : | ||
48 | | ICookie |-------+ : | : | ||
49 | +-----------+ | : | : +-----------+ | ||
50 | | +------------------------------>| IObject | | ||
51 | | : | : +-----------+ | ||
52 | | : V : | | ||
53 | | : +-----------+ : | | ||
54 | V +----------->| IObject | : | | ||
55 | +-----------+ | : +-----------+ : | | ||
56 | | ICookie |-------+ : | : V | ||
57 | +-----------+ | : | : +-----------+ | ||
58 | | +------------------------------>| IObject | | ||
59 | +-----+-----+ : | : +-----------+ | ||
60 | | | : | : | | ||
61 | V | : V : | | ||
62 | +-----------+ | : +-----------+ : | | ||
63 | | ICookie |------------------------->| IObject | : | | ||
64 | +-----------+ | : +-----------+ : | | ||
65 | | V : | : V | ||
66 | | +-----------+ : | : +-----------+ | ||
67 | | | ICookie |-------------------------------->| IObject | | ||
68 | | +-----------+ : | : +-----------+ | ||
69 | V | : V : | | ||
70 | +-----------+ | : +-----------+ : | | ||
71 | | DCookie |------------------------->| DObject | : | | ||
72 | +-----------+ | : +-----------+ : | | ||
73 | | : : | | ||
74 | +-------+-------+ : : | | ||
75 | | | : : | | ||
76 | V V : : V | ||
77 | +-----------+ +-----------+ : : +-----------+ | ||
78 | | DCookie | | DCookie |------------------------>| DObject | | ||
79 | +-----------+ +-----------+ : : +-----------+ | ||
80 | : : | ||
81 | |||
82 | In the above illustration, ICookie and IObject represent indices and DCookie | ||
83 | and DObject represent data storage objects. Indices may have representation in | ||
84 | multiple caches, but currently, non-index objects may not. Objects of any type | ||
85 | may also be entirely unrepresented. | ||
86 | |||
87 | As far as the netfs API goes, the netfs is only actually permitted to see | ||
88 | pointers to the cookies. The cookies themselves and any objects attached to | ||
89 | those cookies are hidden from it. | ||
90 | |||
91 | |||
92 | =============================== | ||
93 | OBJECT MANAGEMENT STATE MACHINE | ||
94 | =============================== | ||
95 | |||
96 | Within FS-Cache, each active object is managed by its own individual state | ||
97 | machine. The state for an object is kept in the fscache_object struct, in | ||
98 | object->state. A cookie may point to a set of objects that are in different | ||
99 | states. | ||
100 | |||
101 | Each state has an action associated with it that is invoked when the machine | ||
102 | wakes up in that state. There are four logical sets of states: | ||
103 | |||
104 | (1) Preparation: states that wait for the parent objects to become ready. The | ||
105 | representations are hierarchical, and it is expected that an object must | ||
106 | be created or accessed with respect to its parent object. | ||
107 | |||
108 | (2) Initialisation: states that perform lookups in the cache and validate | ||
109 | what's found and that create on disk any missing metadata. | ||
110 | |||
111 | (3) Normal running: states that allow netfs operations on objects to proceed | ||
112 | and that update the state of objects. | ||
113 | |||
114 | (4) Termination: states that detach objects from their netfs cookies, that | ||
115 | delete objects from disk, that handle disk and system errors and that free | ||
116 | up in-memory resources. | ||
117 | |||
118 | |||
119 | In most cases, transitioning between states is in response to signalled events. | ||
120 | When a state has finished processing, it will usually set the mask of events in | ||
121 | which it is interested (object->event_mask) and relinquish the worker thread. | ||
122 | Then when an event is raised (by calling fscache_raise_event()), if the event | ||
123 | is not masked, the object will be queued for processing (by calling | ||
124 | fscache_enqueue_object()). | ||
125 | |||
126 | |||
127 | PROVISION OF CPU TIME | ||
128 | --------------------- | ||
129 | |||
130 | The work to be done by the various states is given CPU time by the threads of | ||
131 | the slow work facility (see Documentation/slow-work.txt). This is used in | ||
132 | preference to the workqueue facility because: | ||
133 | |||
134 | (1) Threads may be completely occupied for very long periods of time by a | ||
135 | particular work item. These state actions may be doing sequences of | ||
136 | synchronous, journalled disk accesses (lookup, mkdir, create, setxattr, | ||
137 | getxattr, truncate, unlink, rmdir, rename). | ||
138 | |||
139 | (2) Threads may do little actual work, but may rather spend a lot of time | ||
140 | sleeping on I/O. This means that single-threaded and 1-per-CPU-threaded | ||
141 | workqueues don't necessarily have the right numbers of threads. | ||
142 | |||
143 | |||
144 | LOCKING SIMPLIFICATION | ||
145 | ---------------------- | ||
146 | |||
147 | Because only one worker thread may be operating on any particular object's | ||
148 | state machine at once, this simplifies the locking, particularly with respect | ||
149 | to disconnecting the netfs's representation of a cache object (fscache_cookie) | ||
150 | from the cache backend's representation (fscache_object) - which may be | ||
151 | requested from either end. | ||
152 | |||
153 | |||
154 | ================= | ||
155 | THE SET OF STATES | ||
156 | ================= | ||
157 | |||
158 | The object state machine has a set of states that it can be in. There are | ||
159 | preparation states in which the object sets itself up and waits for its parent | ||
160 | object to transit to a state that allows access to its children: | ||
161 | |||
162 | (1) State FSCACHE_OBJECT_INIT. | ||
163 | |||
164 | Initialise the object and wait for the parent object to become active. In | ||
165 | the cache, it is expected that it will not be possible to look an object | ||
166 | up from the parent object, until that parent object itself has been looked | ||
167 | up. | ||
168 | |||
169 | There are initialisation states in which the object sets itself up and accesses | ||
170 | disk for the object metadata: | ||
171 | |||
172 | (2) State FSCACHE_OBJECT_LOOKING_UP. | ||
173 | |||
174 | Look up the object on disk, using the parent as a starting point. | ||
175 | FS-Cache expects the cache backend to probe the cache to see whether this | ||
176 | object is represented there, and if it is, to see if it's valid (coherency | ||
177 | management). | ||
178 | |||
179 | The cache should call fscache_object_lookup_negative() to indicate lookup | ||
180 | failure for whatever reason, and should call fscache_obtained_object() to | ||
181 | indicate success. | ||
182 | |||
183 | At the completion of lookup, FS-Cache will let the netfs go ahead with | ||
184 | read operations, no matter whether the file is yet cached. If not yet | ||
185 | cached, read operations will be immediately rejected with ENODATA until | ||
186 | the first known page is uncached - as to that point there can be no data | ||
187 | to be read out of the cache for that file that isn't currently also held | ||
188 | in the pagecache. | ||
189 | |||
190 | (3) State FSCACHE_OBJECT_CREATING. | ||
191 | |||
192 | Create an object on disk, using the parent as a starting point. This | ||
193 | happens if the lookup failed to find the object, or if the object's | ||
194 | coherency data indicated what's on disk is out of date. In this state, | ||
195 | FS-Cache expects the cache to create | ||
196 | |||
197 | The cache should call fscache_obtained_object() if creation completes | ||
198 | successfully, fscache_object_lookup_negative() otherwise. | ||
199 | |||
200 | At the completion of creation, FS-Cache will start processing write | ||
201 | operations the netfs has queued for an object. If creation failed, the | ||
202 | write ops will be transparently discarded, and nothing recorded in the | ||
203 | cache. | ||
204 | |||
205 | There are some normal running states in which the object spends its time | ||
206 | servicing netfs requests: | ||
207 | |||
208 | (4) State FSCACHE_OBJECT_AVAILABLE. | ||
209 | |||
210 | A transient state in which pending operations are started, child objects | ||
211 | are permitted to advance from FSCACHE_OBJECT_INIT state, and temporary | ||
212 | lookup data is freed. | ||
213 | |||
214 | (5) State FSCACHE_OBJECT_ACTIVE. | ||
215 | |||
216 | The normal running state. In this state, requests the netfs makes will be | ||
217 | passed on to the cache. | ||
218 | |||
219 | (6) State FSCACHE_OBJECT_UPDATING. | ||
220 | |||
221 | The state machine comes here to update the object in the cache from the | ||
222 | netfs's records. This involves updating the auxiliary data that is used | ||
223 | to maintain coherency. | ||
224 | |||
225 | And there are terminal states in which an object cleans itself up, deallocates | ||
226 | memory and potentially deletes stuff from disk: | ||
227 | |||
228 | (7) State FSCACHE_OBJECT_LC_DYING. | ||
229 | |||
230 | The object comes here if it is dying because of a lookup or creation | ||
231 | error. This would be due to a disk error or system error of some sort. | ||
232 | Temporary data is cleaned up, and the parent is released. | ||
233 | |||
234 | (8) State FSCACHE_OBJECT_DYING. | ||
235 | |||
236 | The object comes here if it is dying due to an error, because its parent | ||
237 | cookie has been relinquished by the netfs or because the cache is being | ||
238 | withdrawn. | ||
239 | |||
240 | Any child objects waiting on this one are given CPU time so that they too | ||
241 | can destroy themselves. This object waits for all its children to go away | ||
242 | before advancing to the next state. | ||
243 | |||
244 | (9) State FSCACHE_OBJECT_ABORT_INIT. | ||
245 | |||
246 | The object comes to this state if it was waiting on its parent in | ||
247 | FSCACHE_OBJECT_INIT, but its parent died. The object will destroy itself | ||
248 | so that the parent may proceed from the FSCACHE_OBJECT_DYING state. | ||
249 | |||
250 | (10) State FSCACHE_OBJECT_RELEASING. | ||
251 | (11) State FSCACHE_OBJECT_RECYCLING. | ||
252 | |||
253 | The object comes to one of these two states when dying once it is rid of | ||
254 | all its children, if it is dying because the netfs relinquished its | ||
255 | cookie. In the first state, the cached data is expected to persist, and | ||
256 | in the second it will be deleted. | ||
257 | |||
258 | (12) State FSCACHE_OBJECT_WITHDRAWING. | ||
259 | |||
260 | The object transits to this state if the cache decides it wants to | ||
261 | withdraw the object from service, perhaps to make space, but also due to | ||
262 | error or just because the whole cache is being withdrawn. | ||
263 | |||
264 | (13) State FSCACHE_OBJECT_DEAD. | ||
265 | |||
266 | The object transits to this state when the in-memory object record is | ||
267 | ready to be deleted. The object processor shouldn't ever see an object in | ||
268 | this state. | ||
269 | |||
270 | |||
271 | THE SET OF EVENTS | ||
272 | ----------------- | ||
273 | |||
274 | There are a number of events that can be raised to an object state machine: | ||
275 | |||
276 | (*) FSCACHE_OBJECT_EV_UPDATE | ||
277 | |||
278 | The netfs requested that an object be updated. The state machine will ask | ||
279 | the cache backend to update the object, and the cache backend will ask the | ||
280 | netfs for details of the change through its cookie definition ops. | ||
281 | |||
282 | (*) FSCACHE_OBJECT_EV_CLEARED | ||
283 | |||
284 | This is signalled in two circumstances: | ||
285 | |||
286 | (a) when an object's last child object is dropped and | ||
287 | |||
288 | (b) when the last operation outstanding on an object is completed. | ||
289 | |||
290 | This is used to proceed from the dying state. | ||
291 | |||
292 | (*) FSCACHE_OBJECT_EV_ERROR | ||
293 | |||
294 | This is signalled when an I/O error occurs during the processing of some | ||
295 | object. | ||
296 | |||
297 | (*) FSCACHE_OBJECT_EV_RELEASE | ||
298 | (*) FSCACHE_OBJECT_EV_RETIRE | ||
299 | |||
300 | These are signalled when the netfs relinquishes a cookie it was using. | ||
301 | The event selected depends on whether the netfs asks for the backing | ||
302 | object to be retired (deleted) or retained. | ||
303 | |||
304 | (*) FSCACHE_OBJECT_EV_WITHDRAW | ||
305 | |||
306 | This is signalled when the cache backend wants to withdraw an object. | ||
307 | This means that the object will have to be detached from the netfs's | ||
308 | cookie. | ||
309 | |||
310 | Because the withdrawing releasing/retiring events are all handled by the object | ||
311 | state machine, it doesn't matter if there's a collision with both ends trying | ||
312 | to sever the connection at the same time. The state machine can just pick | ||
313 | which one it wants to honour, and that effects the other. | ||
diff --git a/fs/fscache/Makefile b/fs/fscache/Makefile index ecf6946eaeb3..4420ac6ea10d 100644 --- a/fs/fscache/Makefile +++ b/fs/fscache/Makefile | |||
@@ -7,7 +7,8 @@ fscache-y := \ | |||
7 | cookie.o \ | 7 | cookie.o \ |
8 | fsdef.o \ | 8 | fsdef.o \ |
9 | main.o \ | 9 | main.o \ |
10 | netfs.o | 10 | netfs.o \ |
11 | object.o | ||
11 | 12 | ||
12 | fscache-$(CONFIG_PROC_FS) += proc.o | 13 | fscache-$(CONFIG_PROC_FS) += proc.o |
13 | fscache-$(CONFIG_FSCACHE_STATS) += stats.o | 14 | fscache-$(CONFIG_FSCACHE_STATS) += stats.o |
diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 16389942a54e..529f4de328c2 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h | |||
@@ -86,6 +86,18 @@ extern int fscache_wait_bit(void *); | |||
86 | extern int fscache_wait_bit_interruptible(void *); | 86 | extern int fscache_wait_bit_interruptible(void *); |
87 | 87 | ||
88 | /* | 88 | /* |
89 | * fsc-object.c | ||
90 | */ | ||
91 | extern void fscache_withdrawing_object(struct fscache_cache *, | ||
92 | struct fscache_object *); | ||
93 | extern void fscache_enqueue_object(struct fscache_object *); | ||
94 | |||
95 | /* | ||
96 | * fsc-operation.c | ||
97 | */ | ||
98 | #define fscache_start_operations(obj) BUG() | ||
99 | |||
100 | /* | ||
89 | * fsc-proc.c | 101 | * fsc-proc.c |
90 | */ | 102 | */ |
91 | #ifdef CONFIG_PROC_FS | 103 | #ifdef CONFIG_PROC_FS |
@@ -196,7 +208,19 @@ extern const struct file_operations fscache_stats_fops; | |||
196 | static inline void fscache_raise_event(struct fscache_object *object, | 208 | static inline void fscache_raise_event(struct fscache_object *object, |
197 | unsigned event) | 209 | unsigned event) |
198 | { | 210 | { |
199 | BUG(); // TODO | 211 | if (!test_and_set_bit(event, &object->events) && |
212 | test_bit(event, &object->event_mask)) | ||
213 | fscache_enqueue_object(object); | ||
214 | } | ||
215 | |||
216 | /* | ||
217 | * drop a reference to a cookie | ||
218 | */ | ||
219 | static inline void fscache_cookie_put(struct fscache_cookie *cookie) | ||
220 | { | ||
221 | BUG_ON(atomic_read(&cookie->usage) <= 0); | ||
222 | if (atomic_dec_and_test(&cookie->usage)) | ||
223 | __fscache_cookie_put(cookie); | ||
200 | } | 224 | } |
201 | 225 | ||
202 | /*****************************************************************************/ | 226 | /*****************************************************************************/ |
diff --git a/fs/fscache/object.c b/fs/fscache/object.c new file mode 100644 index 000000000000..392a41b1b79d --- /dev/null +++ b/fs/fscache/object.c | |||
@@ -0,0 +1,810 @@ | |||
1 | /* FS-Cache object state machine handler | ||
2 | * | ||
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | * | ||
11 | * See Documentation/filesystems/caching/object.txt for a description of the | ||
12 | * object state machine and the in-kernel representations. | ||
13 | */ | ||
14 | |||
15 | #define FSCACHE_DEBUG_LEVEL COOKIE | ||
16 | #include <linux/module.h> | ||
17 | #include "internal.h" | ||
18 | |||
19 | const char *fscache_object_states[] = { | ||
20 | [FSCACHE_OBJECT_INIT] = "OBJECT_INIT", | ||
21 | [FSCACHE_OBJECT_LOOKING_UP] = "OBJECT_LOOKING_UP", | ||
22 | [FSCACHE_OBJECT_CREATING] = "OBJECT_CREATING", | ||
23 | [FSCACHE_OBJECT_AVAILABLE] = "OBJECT_AVAILABLE", | ||
24 | [FSCACHE_OBJECT_ACTIVE] = "OBJECT_ACTIVE", | ||
25 | [FSCACHE_OBJECT_UPDATING] = "OBJECT_UPDATING", | ||
26 | [FSCACHE_OBJECT_DYING] = "OBJECT_DYING", | ||
27 | [FSCACHE_OBJECT_LC_DYING] = "OBJECT_LC_DYING", | ||
28 | [FSCACHE_OBJECT_ABORT_INIT] = "OBJECT_ABORT_INIT", | ||
29 | [FSCACHE_OBJECT_RELEASING] = "OBJECT_RELEASING", | ||
30 | [FSCACHE_OBJECT_RECYCLING] = "OBJECT_RECYCLING", | ||
31 | [FSCACHE_OBJECT_WITHDRAWING] = "OBJECT_WITHDRAWING", | ||
32 | [FSCACHE_OBJECT_DEAD] = "OBJECT_DEAD", | ||
33 | }; | ||
34 | EXPORT_SYMBOL(fscache_object_states); | ||
35 | |||
36 | static void fscache_object_slow_work_put_ref(struct slow_work *); | ||
37 | static int fscache_object_slow_work_get_ref(struct slow_work *); | ||
38 | static void fscache_object_slow_work_execute(struct slow_work *); | ||
39 | static void fscache_initialise_object(struct fscache_object *); | ||
40 | static void fscache_lookup_object(struct fscache_object *); | ||
41 | static void fscache_object_available(struct fscache_object *); | ||
42 | static void fscache_release_object(struct fscache_object *); | ||
43 | static void fscache_withdraw_object(struct fscache_object *); | ||
44 | static void fscache_enqueue_dependents(struct fscache_object *); | ||
45 | static void fscache_dequeue_object(struct fscache_object *); | ||
46 | |||
47 | const struct slow_work_ops fscache_object_slow_work_ops = { | ||
48 | .get_ref = fscache_object_slow_work_get_ref, | ||
49 | .put_ref = fscache_object_slow_work_put_ref, | ||
50 | .execute = fscache_object_slow_work_execute, | ||
51 | }; | ||
52 | EXPORT_SYMBOL(fscache_object_slow_work_ops); | ||
53 | |||
54 | /* | ||
55 | * we need to notify the parent when an op completes that we had outstanding | ||
56 | * upon it | ||
57 | */ | ||
58 | static inline void fscache_done_parent_op(struct fscache_object *object) | ||
59 | { | ||
60 | struct fscache_object *parent = object->parent; | ||
61 | |||
62 | _enter("OBJ%x {OBJ%x,%x}", | ||
63 | object->debug_id, parent->debug_id, parent->n_ops); | ||
64 | |||
65 | spin_lock_nested(&parent->lock, 1); | ||
66 | parent->n_ops--; | ||
67 | parent->n_obj_ops--; | ||
68 | if (parent->n_ops == 0) | ||
69 | fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); | ||
70 | spin_unlock(&parent->lock); | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * process events that have been sent to an object's state machine | ||
75 | * - initiates parent lookup | ||
76 | * - does object lookup | ||
77 | * - does object creation | ||
78 | * - does object recycling and retirement | ||
79 | * - does object withdrawal | ||
80 | */ | ||
81 | static void fscache_object_state_machine(struct fscache_object *object) | ||
82 | { | ||
83 | enum fscache_object_state new_state; | ||
84 | |||
85 | ASSERT(object != NULL); | ||
86 | |||
87 | _enter("{OBJ%x,%s,%lx}", | ||
88 | object->debug_id, fscache_object_states[object->state], | ||
89 | object->events); | ||
90 | |||
91 | switch (object->state) { | ||
92 | /* wait for the parent object to become ready */ | ||
93 | case FSCACHE_OBJECT_INIT: | ||
94 | object->event_mask = | ||
95 | ULONG_MAX & ~(1 << FSCACHE_OBJECT_EV_CLEARED); | ||
96 | fscache_initialise_object(object); | ||
97 | goto done; | ||
98 | |||
99 | /* look up the object metadata on disk */ | ||
100 | case FSCACHE_OBJECT_LOOKING_UP: | ||
101 | fscache_lookup_object(object); | ||
102 | goto lookup_transit; | ||
103 | |||
104 | /* create the object metadata on disk */ | ||
105 | case FSCACHE_OBJECT_CREATING: | ||
106 | fscache_lookup_object(object); | ||
107 | goto lookup_transit; | ||
108 | |||
109 | /* handle an object becoming available; start pending | ||
110 | * operations and queue dependent operations for processing */ | ||
111 | case FSCACHE_OBJECT_AVAILABLE: | ||
112 | fscache_object_available(object); | ||
113 | goto active_transit; | ||
114 | |||
115 | /* normal running state */ | ||
116 | case FSCACHE_OBJECT_ACTIVE: | ||
117 | goto active_transit; | ||
118 | |||
119 | /* update the object metadata on disk */ | ||
120 | case FSCACHE_OBJECT_UPDATING: | ||
121 | clear_bit(FSCACHE_OBJECT_EV_UPDATE, &object->events); | ||
122 | fscache_stat(&fscache_n_updates_run); | ||
123 | object->cache->ops->update_object(object); | ||
124 | goto active_transit; | ||
125 | |||
126 | /* handle an object dying during lookup or creation */ | ||
127 | case FSCACHE_OBJECT_LC_DYING: | ||
128 | object->event_mask &= ~(1 << FSCACHE_OBJECT_EV_UPDATE); | ||
129 | object->cache->ops->lookup_complete(object); | ||
130 | |||
131 | spin_lock(&object->lock); | ||
132 | object->state = FSCACHE_OBJECT_DYING; | ||
133 | if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, | ||
134 | &object->cookie->flags)) | ||
135 | wake_up_bit(&object->cookie->flags, | ||
136 | FSCACHE_COOKIE_CREATING); | ||
137 | spin_unlock(&object->lock); | ||
138 | |||
139 | fscache_done_parent_op(object); | ||
140 | |||
141 | /* wait for completion of all active operations on this object | ||
142 | * and the death of all child objects of this object */ | ||
143 | case FSCACHE_OBJECT_DYING: | ||
144 | dying: | ||
145 | clear_bit(FSCACHE_OBJECT_EV_CLEARED, &object->events); | ||
146 | spin_lock(&object->lock); | ||
147 | _debug("dying OBJ%x {%d,%d}", | ||
148 | object->debug_id, object->n_ops, object->n_children); | ||
149 | if (object->n_ops == 0 && object->n_children == 0) { | ||
150 | object->event_mask &= | ||
151 | ~(1 << FSCACHE_OBJECT_EV_CLEARED); | ||
152 | object->event_mask |= | ||
153 | (1 << FSCACHE_OBJECT_EV_WITHDRAW) | | ||
154 | (1 << FSCACHE_OBJECT_EV_RETIRE) | | ||
155 | (1 << FSCACHE_OBJECT_EV_RELEASE) | | ||
156 | (1 << FSCACHE_OBJECT_EV_ERROR); | ||
157 | } else { | ||
158 | object->event_mask &= | ||
159 | ~((1 << FSCACHE_OBJECT_EV_WITHDRAW) | | ||
160 | (1 << FSCACHE_OBJECT_EV_RETIRE) | | ||
161 | (1 << FSCACHE_OBJECT_EV_RELEASE) | | ||
162 | (1 << FSCACHE_OBJECT_EV_ERROR)); | ||
163 | object->event_mask |= | ||
164 | 1 << FSCACHE_OBJECT_EV_CLEARED; | ||
165 | } | ||
166 | spin_unlock(&object->lock); | ||
167 | fscache_enqueue_dependents(object); | ||
168 | goto terminal_transit; | ||
169 | |||
170 | /* handle an abort during initialisation */ | ||
171 | case FSCACHE_OBJECT_ABORT_INIT: | ||
172 | _debug("handle abort init %lx", object->events); | ||
173 | object->event_mask &= ~(1 << FSCACHE_OBJECT_EV_UPDATE); | ||
174 | |||
175 | spin_lock(&object->lock); | ||
176 | fscache_dequeue_object(object); | ||
177 | |||
178 | object->state = FSCACHE_OBJECT_DYING; | ||
179 | if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, | ||
180 | &object->cookie->flags)) | ||
181 | wake_up_bit(&object->cookie->flags, | ||
182 | FSCACHE_COOKIE_CREATING); | ||
183 | spin_unlock(&object->lock); | ||
184 | goto dying; | ||
185 | |||
186 | /* handle the netfs releasing an object and possibly marking it | ||
187 | * obsolete too */ | ||
188 | case FSCACHE_OBJECT_RELEASING: | ||
189 | case FSCACHE_OBJECT_RECYCLING: | ||
190 | object->event_mask &= | ||
191 | ~((1 << FSCACHE_OBJECT_EV_WITHDRAW) | | ||
192 | (1 << FSCACHE_OBJECT_EV_RETIRE) | | ||
193 | (1 << FSCACHE_OBJECT_EV_RELEASE) | | ||
194 | (1 << FSCACHE_OBJECT_EV_ERROR)); | ||
195 | fscache_release_object(object); | ||
196 | spin_lock(&object->lock); | ||
197 | object->state = FSCACHE_OBJECT_DEAD; | ||
198 | spin_unlock(&object->lock); | ||
199 | fscache_stat(&fscache_n_object_dead); | ||
200 | goto terminal_transit; | ||
201 | |||
202 | /* handle the parent cache of this object being withdrawn from | ||
203 | * active service */ | ||
204 | case FSCACHE_OBJECT_WITHDRAWING: | ||
205 | object->event_mask &= | ||
206 | ~((1 << FSCACHE_OBJECT_EV_WITHDRAW) | | ||
207 | (1 << FSCACHE_OBJECT_EV_RETIRE) | | ||
208 | (1 << FSCACHE_OBJECT_EV_RELEASE) | | ||
209 | (1 << FSCACHE_OBJECT_EV_ERROR)); | ||
210 | fscache_withdraw_object(object); | ||
211 | spin_lock(&object->lock); | ||
212 | object->state = FSCACHE_OBJECT_DEAD; | ||
213 | spin_unlock(&object->lock); | ||
214 | fscache_stat(&fscache_n_object_dead); | ||
215 | goto terminal_transit; | ||
216 | |||
217 | /* complain about the object being woken up once it is | ||
218 | * deceased */ | ||
219 | case FSCACHE_OBJECT_DEAD: | ||
220 | printk(KERN_ERR "FS-Cache:" | ||
221 | " Unexpected event in dead state %lx\n", | ||
222 | object->events & object->event_mask); | ||
223 | BUG(); | ||
224 | |||
225 | default: | ||
226 | printk(KERN_ERR "FS-Cache: Unknown object state %u\n", | ||
227 | object->state); | ||
228 | BUG(); | ||
229 | } | ||
230 | |||
231 | /* determine the transition from a lookup state */ | ||
232 | lookup_transit: | ||
233 | switch (fls(object->events & object->event_mask) - 1) { | ||
234 | case FSCACHE_OBJECT_EV_WITHDRAW: | ||
235 | case FSCACHE_OBJECT_EV_RETIRE: | ||
236 | case FSCACHE_OBJECT_EV_RELEASE: | ||
237 | case FSCACHE_OBJECT_EV_ERROR: | ||
238 | new_state = FSCACHE_OBJECT_LC_DYING; | ||
239 | goto change_state; | ||
240 | case FSCACHE_OBJECT_EV_REQUEUE: | ||
241 | goto done; | ||
242 | case -1: | ||
243 | goto done; /* sleep until event */ | ||
244 | default: | ||
245 | goto unsupported_event; | ||
246 | } | ||
247 | |||
248 | /* determine the transition from an active state */ | ||
249 | active_transit: | ||
250 | switch (fls(object->events & object->event_mask) - 1) { | ||
251 | case FSCACHE_OBJECT_EV_WITHDRAW: | ||
252 | case FSCACHE_OBJECT_EV_RETIRE: | ||
253 | case FSCACHE_OBJECT_EV_RELEASE: | ||
254 | case FSCACHE_OBJECT_EV_ERROR: | ||
255 | new_state = FSCACHE_OBJECT_DYING; | ||
256 | goto change_state; | ||
257 | case FSCACHE_OBJECT_EV_UPDATE: | ||
258 | new_state = FSCACHE_OBJECT_UPDATING; | ||
259 | goto change_state; | ||
260 | case -1: | ||
261 | new_state = FSCACHE_OBJECT_ACTIVE; | ||
262 | goto change_state; /* sleep until event */ | ||
263 | default: | ||
264 | goto unsupported_event; | ||
265 | } | ||
266 | |||
267 | /* determine the transition from a terminal state */ | ||
268 | terminal_transit: | ||
269 | switch (fls(object->events & object->event_mask) - 1) { | ||
270 | case FSCACHE_OBJECT_EV_WITHDRAW: | ||
271 | new_state = FSCACHE_OBJECT_WITHDRAWING; | ||
272 | goto change_state; | ||
273 | case FSCACHE_OBJECT_EV_RETIRE: | ||
274 | new_state = FSCACHE_OBJECT_RECYCLING; | ||
275 | goto change_state; | ||
276 | case FSCACHE_OBJECT_EV_RELEASE: | ||
277 | new_state = FSCACHE_OBJECT_RELEASING; | ||
278 | goto change_state; | ||
279 | case FSCACHE_OBJECT_EV_ERROR: | ||
280 | new_state = FSCACHE_OBJECT_WITHDRAWING; | ||
281 | goto change_state; | ||
282 | case FSCACHE_OBJECT_EV_CLEARED: | ||
283 | new_state = FSCACHE_OBJECT_DYING; | ||
284 | goto change_state; | ||
285 | case -1: | ||
286 | goto done; /* sleep until event */ | ||
287 | default: | ||
288 | goto unsupported_event; | ||
289 | } | ||
290 | |||
291 | change_state: | ||
292 | spin_lock(&object->lock); | ||
293 | object->state = new_state; | ||
294 | spin_unlock(&object->lock); | ||
295 | |||
296 | done: | ||
297 | _leave(" [->%s]", fscache_object_states[object->state]); | ||
298 | return; | ||
299 | |||
300 | unsupported_event: | ||
301 | printk(KERN_ERR "FS-Cache:" | ||
302 | " Unsupported event %lx [mask %lx] in state %s\n", | ||
303 | object->events, object->event_mask, | ||
304 | fscache_object_states[object->state]); | ||
305 | BUG(); | ||
306 | } | ||
307 | |||
308 | /* | ||
309 | * execute an object | ||
310 | */ | ||
311 | static void fscache_object_slow_work_execute(struct slow_work *work) | ||
312 | { | ||
313 | struct fscache_object *object = | ||
314 | container_of(work, struct fscache_object, work); | ||
315 | unsigned long start; | ||
316 | |||
317 | _enter("{OBJ%x}", object->debug_id); | ||
318 | |||
319 | clear_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
320 | |||
321 | start = jiffies; | ||
322 | fscache_object_state_machine(object); | ||
323 | fscache_hist(fscache_objs_histogram, start); | ||
324 | if (object->events & object->event_mask) | ||
325 | fscache_enqueue_object(object); | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * initialise an object | ||
330 | * - check the specified object's parent to see if we can make use of it | ||
331 | * immediately to do a creation | ||
332 | * - we may need to start the process of creating a parent and we need to wait | ||
333 | * for the parent's lookup and creation to complete if it's not there yet | ||
334 | * - an object's cookie is pinned until we clear FSCACHE_COOKIE_CREATING on the | ||
335 | * leaf-most cookies of the object and all its children | ||
336 | */ | ||
337 | static void fscache_initialise_object(struct fscache_object *object) | ||
338 | { | ||
339 | struct fscache_object *parent; | ||
340 | |||
341 | _enter(""); | ||
342 | ASSERT(object->cookie != NULL); | ||
343 | ASSERT(object->cookie->parent != NULL); | ||
344 | ASSERT(list_empty(&object->work.link)); | ||
345 | |||
346 | if (object->events & ((1 << FSCACHE_OBJECT_EV_ERROR) | | ||
347 | (1 << FSCACHE_OBJECT_EV_RELEASE) | | ||
348 | (1 << FSCACHE_OBJECT_EV_RETIRE) | | ||
349 | (1 << FSCACHE_OBJECT_EV_WITHDRAW))) { | ||
350 | _debug("abort init %lx", object->events); | ||
351 | spin_lock(&object->lock); | ||
352 | object->state = FSCACHE_OBJECT_ABORT_INIT; | ||
353 | spin_unlock(&object->lock); | ||
354 | return; | ||
355 | } | ||
356 | |||
357 | spin_lock(&object->cookie->lock); | ||
358 | spin_lock_nested(&object->cookie->parent->lock, 1); | ||
359 | |||
360 | parent = object->parent; | ||
361 | if (!parent) { | ||
362 | _debug("no parent"); | ||
363 | set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); | ||
364 | } else { | ||
365 | spin_lock(&object->lock); | ||
366 | spin_lock_nested(&parent->lock, 1); | ||
367 | _debug("parent %s", fscache_object_states[parent->state]); | ||
368 | |||
369 | if (parent->state >= FSCACHE_OBJECT_DYING) { | ||
370 | _debug("bad parent"); | ||
371 | set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); | ||
372 | } else if (parent->state < FSCACHE_OBJECT_AVAILABLE) { | ||
373 | _debug("wait"); | ||
374 | |||
375 | /* we may get woken up in this state by child objects | ||
376 | * binding on to us, so we need to make sure we don't | ||
377 | * add ourself to the list multiple times */ | ||
378 | if (list_empty(&object->dep_link)) { | ||
379 | object->cache->ops->grab_object(object); | ||
380 | list_add(&object->dep_link, | ||
381 | &parent->dependents); | ||
382 | |||
383 | /* fscache_acquire_non_index_cookie() uses this | ||
384 | * to wake the chain up */ | ||
385 | if (parent->state == FSCACHE_OBJECT_INIT) | ||
386 | fscache_enqueue_object(parent); | ||
387 | } | ||
388 | } else { | ||
389 | _debug("go"); | ||
390 | parent->n_ops++; | ||
391 | parent->n_obj_ops++; | ||
392 | object->lookup_jif = jiffies; | ||
393 | object->state = FSCACHE_OBJECT_LOOKING_UP; | ||
394 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
395 | } | ||
396 | |||
397 | spin_unlock(&parent->lock); | ||
398 | spin_unlock(&object->lock); | ||
399 | } | ||
400 | |||
401 | spin_unlock(&object->cookie->parent->lock); | ||
402 | spin_unlock(&object->cookie->lock); | ||
403 | _leave(""); | ||
404 | } | ||
405 | |||
406 | /* | ||
407 | * look an object up in the cache from which it was allocated | ||
408 | * - we hold an "access lock" on the parent object, so the parent object cannot | ||
409 | * be withdrawn by either party till we've finished | ||
410 | * - an object's cookie is pinned until we clear FSCACHE_COOKIE_CREATING on the | ||
411 | * leaf-most cookies of the object and all its children | ||
412 | */ | ||
413 | static void fscache_lookup_object(struct fscache_object *object) | ||
414 | { | ||
415 | struct fscache_cookie *cookie = object->cookie; | ||
416 | struct fscache_object *parent; | ||
417 | |||
418 | _enter(""); | ||
419 | |||
420 | parent = object->parent; | ||
421 | ASSERT(parent != NULL); | ||
422 | ASSERTCMP(parent->n_ops, >, 0); | ||
423 | ASSERTCMP(parent->n_obj_ops, >, 0); | ||
424 | |||
425 | /* make sure the parent is still available */ | ||
426 | ASSERTCMP(parent->state, >=, FSCACHE_OBJECT_AVAILABLE); | ||
427 | |||
428 | if (parent->state >= FSCACHE_OBJECT_DYING || | ||
429 | test_bit(FSCACHE_IOERROR, &object->cache->flags)) { | ||
430 | _debug("unavailable"); | ||
431 | set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); | ||
432 | _leave(""); | ||
433 | return; | ||
434 | } | ||
435 | |||
436 | _debug("LOOKUP \"%s/%s\" in \"%s\"", | ||
437 | parent->cookie->def->name, cookie->def->name, | ||
438 | object->cache->tag->name); | ||
439 | |||
440 | fscache_stat(&fscache_n_object_lookups); | ||
441 | object->cache->ops->lookup_object(object); | ||
442 | |||
443 | if (test_bit(FSCACHE_OBJECT_EV_ERROR, &object->events)) | ||
444 | set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); | ||
445 | |||
446 | _leave(""); | ||
447 | } | ||
448 | |||
449 | /** | ||
450 | * fscache_object_lookup_negative - Note negative cookie lookup | ||
451 | * @object: Object pointing to cookie to mark | ||
452 | * | ||
453 | * Note negative lookup, permitting those waiting to read data from an already | ||
454 | * existing backing object to continue as there's no data for them to read. | ||
455 | */ | ||
456 | void fscache_object_lookup_negative(struct fscache_object *object) | ||
457 | { | ||
458 | struct fscache_cookie *cookie = object->cookie; | ||
459 | |||
460 | _enter("{OBJ%x,%s}", | ||
461 | object->debug_id, fscache_object_states[object->state]); | ||
462 | |||
463 | spin_lock(&object->lock); | ||
464 | if (object->state == FSCACHE_OBJECT_LOOKING_UP) { | ||
465 | fscache_stat(&fscache_n_object_lookups_negative); | ||
466 | |||
467 | /* transit here to allow write requests to begin stacking up | ||
468 | * and read requests to begin returning ENODATA */ | ||
469 | object->state = FSCACHE_OBJECT_CREATING; | ||
470 | spin_unlock(&object->lock); | ||
471 | |||
472 | set_bit(FSCACHE_COOKIE_PENDING_FILL, &cookie->flags); | ||
473 | set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); | ||
474 | |||
475 | _debug("wake up lookup %p", &cookie->flags); | ||
476 | smp_mb__before_clear_bit(); | ||
477 | clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); | ||
478 | smp_mb__after_clear_bit(); | ||
479 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); | ||
480 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
481 | } else { | ||
482 | ASSERTCMP(object->state, ==, FSCACHE_OBJECT_CREATING); | ||
483 | spin_unlock(&object->lock); | ||
484 | } | ||
485 | |||
486 | _leave(""); | ||
487 | } | ||
488 | EXPORT_SYMBOL(fscache_object_lookup_negative); | ||
489 | |||
490 | /** | ||
491 | * fscache_obtained_object - Note successful object lookup or creation | ||
492 | * @object: Object pointing to cookie to mark | ||
493 | * | ||
494 | * Note successful lookup and/or creation, permitting those waiting to write | ||
495 | * data to a backing object to continue. | ||
496 | * | ||
497 | * Note that after calling this, an object's cookie may be relinquished by the | ||
498 | * netfs, and so must be accessed with object lock held. | ||
499 | */ | ||
500 | void fscache_obtained_object(struct fscache_object *object) | ||
501 | { | ||
502 | struct fscache_cookie *cookie = object->cookie; | ||
503 | |||
504 | _enter("{OBJ%x,%s}", | ||
505 | object->debug_id, fscache_object_states[object->state]); | ||
506 | |||
507 | /* if we were still looking up, then we must have a positive lookup | ||
508 | * result, in which case there may be data available */ | ||
509 | spin_lock(&object->lock); | ||
510 | if (object->state == FSCACHE_OBJECT_LOOKING_UP) { | ||
511 | fscache_stat(&fscache_n_object_lookups_positive); | ||
512 | |||
513 | clear_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); | ||
514 | |||
515 | object->state = FSCACHE_OBJECT_AVAILABLE; | ||
516 | spin_unlock(&object->lock); | ||
517 | |||
518 | smp_mb__before_clear_bit(); | ||
519 | clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); | ||
520 | smp_mb__after_clear_bit(); | ||
521 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); | ||
522 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
523 | } else { | ||
524 | ASSERTCMP(object->state, ==, FSCACHE_OBJECT_CREATING); | ||
525 | fscache_stat(&fscache_n_object_created); | ||
526 | |||
527 | object->state = FSCACHE_OBJECT_AVAILABLE; | ||
528 | spin_unlock(&object->lock); | ||
529 | set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); | ||
530 | smp_wmb(); | ||
531 | } | ||
532 | |||
533 | if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, &cookie->flags)) | ||
534 | wake_up_bit(&cookie->flags, FSCACHE_COOKIE_CREATING); | ||
535 | |||
536 | _leave(""); | ||
537 | } | ||
538 | EXPORT_SYMBOL(fscache_obtained_object); | ||
539 | |||
540 | /* | ||
541 | * handle an object that has just become available | ||
542 | */ | ||
543 | static void fscache_object_available(struct fscache_object *object) | ||
544 | { | ||
545 | _enter("{OBJ%x}", object->debug_id); | ||
546 | |||
547 | spin_lock(&object->lock); | ||
548 | |||
549 | if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, &object->cookie->flags)) | ||
550 | wake_up_bit(&object->cookie->flags, FSCACHE_COOKIE_CREATING); | ||
551 | |||
552 | fscache_done_parent_op(object); | ||
553 | if (object->n_in_progress == 0) { | ||
554 | if (object->n_ops > 0) { | ||
555 | ASSERTCMP(object->n_ops, >=, object->n_obj_ops); | ||
556 | ASSERTIF(object->n_ops > object->n_obj_ops, | ||
557 | !list_empty(&object->pending_ops)); | ||
558 | fscache_start_operations(object); | ||
559 | } else { | ||
560 | ASSERT(list_empty(&object->pending_ops)); | ||
561 | } | ||
562 | } | ||
563 | spin_unlock(&object->lock); | ||
564 | |||
565 | object->cache->ops->lookup_complete(object); | ||
566 | fscache_enqueue_dependents(object); | ||
567 | |||
568 | fscache_hist(fscache_obj_instantiate_histogram, object->lookup_jif); | ||
569 | fscache_stat(&fscache_n_object_avail); | ||
570 | |||
571 | _leave(""); | ||
572 | } | ||
573 | |||
574 | /* | ||
575 | * drop an object's attachments | ||
576 | */ | ||
577 | static void fscache_drop_object(struct fscache_object *object) | ||
578 | { | ||
579 | struct fscache_object *parent = object->parent; | ||
580 | struct fscache_cache *cache = object->cache; | ||
581 | |||
582 | _enter("{OBJ%x,%d}", object->debug_id, object->n_children); | ||
583 | |||
584 | spin_lock(&cache->object_list_lock); | ||
585 | list_del_init(&object->cache_link); | ||
586 | spin_unlock(&cache->object_list_lock); | ||
587 | |||
588 | cache->ops->drop_object(object); | ||
589 | |||
590 | if (parent) { | ||
591 | _debug("release parent OBJ%x {%d}", | ||
592 | parent->debug_id, parent->n_children); | ||
593 | |||
594 | spin_lock(&parent->lock); | ||
595 | parent->n_children--; | ||
596 | if (parent->n_children == 0) | ||
597 | fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); | ||
598 | spin_unlock(&parent->lock); | ||
599 | object->parent = NULL; | ||
600 | } | ||
601 | |||
602 | /* this just shifts the object release to the slow work processor */ | ||
603 | object->cache->ops->put_object(object); | ||
604 | |||
605 | _leave(""); | ||
606 | } | ||
607 | |||
608 | /* | ||
609 | * release or recycle an object that the netfs has discarded | ||
610 | */ | ||
611 | static void fscache_release_object(struct fscache_object *object) | ||
612 | { | ||
613 | _enter(""); | ||
614 | |||
615 | fscache_drop_object(object); | ||
616 | } | ||
617 | |||
618 | /* | ||
619 | * withdraw an object from active service | ||
620 | */ | ||
621 | static void fscache_withdraw_object(struct fscache_object *object) | ||
622 | { | ||
623 | struct fscache_cookie *cookie; | ||
624 | bool detached; | ||
625 | |||
626 | _enter(""); | ||
627 | |||
628 | spin_lock(&object->lock); | ||
629 | cookie = object->cookie; | ||
630 | if (cookie) { | ||
631 | /* need to get the cookie lock before the object lock, starting | ||
632 | * from the object pointer */ | ||
633 | atomic_inc(&cookie->usage); | ||
634 | spin_unlock(&object->lock); | ||
635 | |||
636 | detached = false; | ||
637 | spin_lock(&cookie->lock); | ||
638 | spin_lock(&object->lock); | ||
639 | |||
640 | if (object->cookie == cookie) { | ||
641 | hlist_del_init(&object->cookie_link); | ||
642 | object->cookie = NULL; | ||
643 | detached = true; | ||
644 | } | ||
645 | spin_unlock(&cookie->lock); | ||
646 | fscache_cookie_put(cookie); | ||
647 | if (detached) | ||
648 | fscache_cookie_put(cookie); | ||
649 | } | ||
650 | |||
651 | spin_unlock(&object->lock); | ||
652 | |||
653 | fscache_drop_object(object); | ||
654 | } | ||
655 | |||
656 | /* | ||
657 | * withdraw an object from active service at the behest of the cache | ||
658 | * - need break the links to a cached object cookie | ||
659 | * - called under two situations: | ||
660 | * (1) recycler decides to reclaim an in-use object | ||
661 | * (2) a cache is unmounted | ||
662 | * - have to take care as the cookie can be being relinquished by the netfs | ||
663 | * simultaneously | ||
664 | * - the object is pinned by the caller holding a refcount on it | ||
665 | */ | ||
666 | void fscache_withdrawing_object(struct fscache_cache *cache, | ||
667 | struct fscache_object *object) | ||
668 | { | ||
669 | bool enqueue = false; | ||
670 | |||
671 | _enter(",OBJ%x", object->debug_id); | ||
672 | |||
673 | spin_lock(&object->lock); | ||
674 | if (object->state < FSCACHE_OBJECT_WITHDRAWING) { | ||
675 | object->state = FSCACHE_OBJECT_WITHDRAWING; | ||
676 | enqueue = true; | ||
677 | } | ||
678 | spin_unlock(&object->lock); | ||
679 | |||
680 | if (enqueue) | ||
681 | fscache_enqueue_object(object); | ||
682 | |||
683 | _leave(""); | ||
684 | } | ||
685 | |||
686 | /* | ||
687 | * allow the slow work item processor to get a ref on an object | ||
688 | */ | ||
689 | static int fscache_object_slow_work_get_ref(struct slow_work *work) | ||
690 | { | ||
691 | struct fscache_object *object = | ||
692 | container_of(work, struct fscache_object, work); | ||
693 | |||
694 | return object->cache->ops->grab_object(object) ? 0 : -EAGAIN; | ||
695 | } | ||
696 | |||
697 | /* | ||
698 | * allow the slow work item processor to discard a ref on a work item | ||
699 | */ | ||
700 | static void fscache_object_slow_work_put_ref(struct slow_work *work) | ||
701 | { | ||
702 | struct fscache_object *object = | ||
703 | container_of(work, struct fscache_object, work); | ||
704 | |||
705 | return object->cache->ops->put_object(object); | ||
706 | } | ||
707 | |||
708 | /* | ||
709 | * enqueue an object for metadata-type processing | ||
710 | */ | ||
711 | void fscache_enqueue_object(struct fscache_object *object) | ||
712 | { | ||
713 | _enter("{OBJ%x}", object->debug_id); | ||
714 | |||
715 | slow_work_enqueue(&object->work); | ||
716 | } | ||
717 | |||
718 | /* | ||
719 | * enqueue the dependents of an object for metadata-type processing | ||
720 | * - the caller must hold the object's lock | ||
721 | * - this may cause an already locked object to wind up being processed again | ||
722 | */ | ||
723 | static void fscache_enqueue_dependents(struct fscache_object *object) | ||
724 | { | ||
725 | struct fscache_object *dep; | ||
726 | |||
727 | _enter("{OBJ%x}", object->debug_id); | ||
728 | |||
729 | if (list_empty(&object->dependents)) | ||
730 | return; | ||
731 | |||
732 | spin_lock(&object->lock); | ||
733 | |||
734 | while (!list_empty(&object->dependents)) { | ||
735 | dep = list_entry(object->dependents.next, | ||
736 | struct fscache_object, dep_link); | ||
737 | list_del_init(&dep->dep_link); | ||
738 | |||
739 | |||
740 | /* sort onto appropriate lists */ | ||
741 | fscache_enqueue_object(dep); | ||
742 | dep->cache->ops->put_object(dep); | ||
743 | |||
744 | if (!list_empty(&object->dependents)) | ||
745 | cond_resched_lock(&object->lock); | ||
746 | } | ||
747 | |||
748 | spin_unlock(&object->lock); | ||
749 | } | ||
750 | |||
751 | /* | ||
752 | * remove an object from whatever queue it's waiting on | ||
753 | * - the caller must hold object->lock | ||
754 | */ | ||
755 | void fscache_dequeue_object(struct fscache_object *object) | ||
756 | { | ||
757 | _enter("{OBJ%x}", object->debug_id); | ||
758 | |||
759 | if (!list_empty(&object->dep_link)) { | ||
760 | spin_lock(&object->parent->lock); | ||
761 | list_del_init(&object->dep_link); | ||
762 | spin_unlock(&object->parent->lock); | ||
763 | } | ||
764 | |||
765 | _leave(""); | ||
766 | } | ||
767 | |||
768 | /** | ||
769 | * fscache_check_aux - Ask the netfs whether an object on disk is still valid | ||
770 | * @object: The object to ask about | ||
771 | * @data: The auxiliary data for the object | ||
772 | * @datalen: The size of the auxiliary data | ||
773 | * | ||
774 | * This function consults the netfs about the coherency state of an object | ||
775 | */ | ||
776 | enum fscache_checkaux fscache_check_aux(struct fscache_object *object, | ||
777 | const void *data, uint16_t datalen) | ||
778 | { | ||
779 | enum fscache_checkaux result; | ||
780 | |||
781 | if (!object->cookie->def->check_aux) { | ||
782 | fscache_stat(&fscache_n_checkaux_none); | ||
783 | return FSCACHE_CHECKAUX_OKAY; | ||
784 | } | ||
785 | |||
786 | result = object->cookie->def->check_aux(object->cookie->netfs_data, | ||
787 | data, datalen); | ||
788 | switch (result) { | ||
789 | /* entry okay as is */ | ||
790 | case FSCACHE_CHECKAUX_OKAY: | ||
791 | fscache_stat(&fscache_n_checkaux_okay); | ||
792 | break; | ||
793 | |||
794 | /* entry requires update */ | ||
795 | case FSCACHE_CHECKAUX_NEEDS_UPDATE: | ||
796 | fscache_stat(&fscache_n_checkaux_update); | ||
797 | break; | ||
798 | |||
799 | /* entry requires deletion */ | ||
800 | case FSCACHE_CHECKAUX_OBSOLETE: | ||
801 | fscache_stat(&fscache_n_checkaux_obsolete); | ||
802 | break; | ||
803 | |||
804 | default: | ||
805 | BUG(); | ||
806 | } | ||
807 | |||
808 | return result; | ||
809 | } | ||
810 | EXPORT_SYMBOL(fscache_check_aux); | ||