]>
Commit | Line | Data |
---|---|---|
0e04d4ce DH |
1 | /* FS-Cache cache handling |
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 | ||
12 | #define FSCACHE_DEBUG_LEVEL CACHE | |
13 | #include <linux/module.h> | |
14 | #include <linux/slab.h> | |
15 | #include "internal.h" | |
16 | ||
17 | LIST_HEAD(fscache_cache_list); | |
18 | DECLARE_RWSEM(fscache_addremove_sem); | |
4c515dd4 DH |
19 | DECLARE_WAIT_QUEUE_HEAD(fscache_cache_cleared_wq); |
20 | EXPORT_SYMBOL(fscache_cache_cleared_wq); | |
0e04d4ce DH |
21 | |
22 | static LIST_HEAD(fscache_cache_tag_list); | |
23 | ||
24 | /* | |
25 | * look up a cache tag | |
26 | */ | |
27 | struct fscache_cache_tag *__fscache_lookup_cache_tag(const char *name) | |
28 | { | |
29 | struct fscache_cache_tag *tag, *xtag; | |
30 | ||
31 | /* firstly check for the existence of the tag under read lock */ | |
32 | down_read(&fscache_addremove_sem); | |
33 | ||
34 | list_for_each_entry(tag, &fscache_cache_tag_list, link) { | |
35 | if (strcmp(tag->name, name) == 0) { | |
36 | atomic_inc(&tag->usage); | |
37 | up_read(&fscache_addremove_sem); | |
38 | return tag; | |
39 | } | |
40 | } | |
41 | ||
42 | up_read(&fscache_addremove_sem); | |
43 | ||
44 | /* the tag does not exist - create a candidate */ | |
45 | xtag = kzalloc(sizeof(*xtag) + strlen(name) + 1, GFP_KERNEL); | |
46 | if (!xtag) | |
47 | /* return a dummy tag if out of memory */ | |
48 | return ERR_PTR(-ENOMEM); | |
49 | ||
50 | atomic_set(&xtag->usage, 1); | |
51 | strcpy(xtag->name, name); | |
52 | ||
53 | /* write lock, search again and add if still not present */ | |
54 | down_write(&fscache_addremove_sem); | |
55 | ||
56 | list_for_each_entry(tag, &fscache_cache_tag_list, link) { | |
57 | if (strcmp(tag->name, name) == 0) { | |
58 | atomic_inc(&tag->usage); | |
59 | up_write(&fscache_addremove_sem); | |
60 | kfree(xtag); | |
61 | return tag; | |
62 | } | |
63 | } | |
64 | ||
65 | list_add_tail(&xtag->link, &fscache_cache_tag_list); | |
66 | up_write(&fscache_addremove_sem); | |
67 | return xtag; | |
68 | } | |
69 | ||
70 | /* | |
71 | * release a reference to a cache tag | |
72 | */ | |
73 | void __fscache_release_cache_tag(struct fscache_cache_tag *tag) | |
74 | { | |
75 | if (tag != ERR_PTR(-ENOMEM)) { | |
76 | down_write(&fscache_addremove_sem); | |
77 | ||
78 | if (atomic_dec_and_test(&tag->usage)) | |
79 | list_del_init(&tag->link); | |
80 | else | |
81 | tag = NULL; | |
82 | ||
83 | up_write(&fscache_addremove_sem); | |
84 | ||
85 | kfree(tag); | |
86 | } | |
87 | } | |
88 | ||
89 | /* | |
90 | * select a cache in which to store an object | |
91 | * - the cache addremove semaphore must be at least read-locked by the caller | |
92 | * - the object will never be an index | |
93 | */ | |
94 | struct fscache_cache *fscache_select_cache_for_object( | |
95 | struct fscache_cookie *cookie) | |
96 | { | |
97 | struct fscache_cache_tag *tag; | |
98 | struct fscache_object *object; | |
99 | struct fscache_cache *cache; | |
100 | ||
101 | _enter(""); | |
102 | ||
103 | if (list_empty(&fscache_cache_list)) { | |
104 | _leave(" = NULL [no cache]"); | |
105 | return NULL; | |
106 | } | |
107 | ||
108 | /* we check the parent to determine the cache to use */ | |
109 | spin_lock(&cookie->lock); | |
110 | ||
111 | /* the first in the parent's backing list should be the preferred | |
112 | * cache */ | |
113 | if (!hlist_empty(&cookie->backing_objects)) { | |
114 | object = hlist_entry(cookie->backing_objects.first, | |
115 | struct fscache_object, cookie_link); | |
116 | ||
117 | cache = object->cache; | |
493f7bc1 | 118 | if (fscache_object_is_dying(object) || |
0e04d4ce DH |
119 | test_bit(FSCACHE_IOERROR, &cache->flags)) |
120 | cache = NULL; | |
121 | ||
122 | spin_unlock(&cookie->lock); | |
123 | _leave(" = %p [parent]", cache); | |
124 | return cache; | |
125 | } | |
126 | ||
127 | /* the parent is unbacked */ | |
402cb8dd | 128 | if (cookie->type != FSCACHE_COOKIE_TYPE_INDEX) { |
0e04d4ce DH |
129 | /* cookie not an index and is unbacked */ |
130 | spin_unlock(&cookie->lock); | |
131 | _leave(" = NULL [cookie ub,ni]"); | |
132 | return NULL; | |
133 | } | |
134 | ||
135 | spin_unlock(&cookie->lock); | |
136 | ||
137 | if (!cookie->def->select_cache) | |
138 | goto no_preference; | |
139 | ||
140 | /* ask the netfs for its preference */ | |
141 | tag = cookie->def->select_cache(cookie->parent->netfs_data, | |
142 | cookie->netfs_data); | |
143 | if (!tag) | |
144 | goto no_preference; | |
145 | ||
146 | if (tag == ERR_PTR(-ENOMEM)) { | |
147 | _leave(" = NULL [nomem tag]"); | |
148 | return NULL; | |
149 | } | |
150 | ||
151 | if (!tag->cache) { | |
152 | _leave(" = NULL [unbacked tag]"); | |
153 | return NULL; | |
154 | } | |
155 | ||
156 | if (test_bit(FSCACHE_IOERROR, &tag->cache->flags)) | |
157 | return NULL; | |
158 | ||
159 | _leave(" = %p [specific]", tag->cache); | |
160 | return tag->cache; | |
161 | ||
162 | no_preference: | |
163 | /* netfs has no preference - just select first cache */ | |
164 | cache = list_entry(fscache_cache_list.next, | |
165 | struct fscache_cache, link); | |
166 | _leave(" = %p [first]", cache); | |
167 | return cache; | |
168 | } | |
4c515dd4 DH |
169 | |
170 | /** | |
171 | * fscache_init_cache - Initialise a cache record | |
172 | * @cache: The cache record to be initialised | |
173 | * @ops: The cache operations to be installed in that record | |
174 | * @idfmt: Format string to define identifier | |
175 | * @...: sprintf-style arguments | |
176 | * | |
177 | * Initialise a record of a cache and fill in the name. | |
178 | * | |
179 | * See Documentation/filesystems/caching/backend-api.txt for a complete | |
180 | * description. | |
181 | */ | |
182 | void fscache_init_cache(struct fscache_cache *cache, | |
183 | const struct fscache_cache_ops *ops, | |
184 | const char *idfmt, | |
185 | ...) | |
186 | { | |
187 | va_list va; | |
188 | ||
189 | memset(cache, 0, sizeof(*cache)); | |
190 | ||
191 | cache->ops = ops; | |
192 | ||
193 | va_start(va, idfmt); | |
194 | vsnprintf(cache->identifier, sizeof(cache->identifier), idfmt, va); | |
195 | va_end(va); | |
196 | ||
952efe7b | 197 | INIT_WORK(&cache->op_gc, fscache_operation_gc); |
4c515dd4 DH |
198 | INIT_LIST_HEAD(&cache->link); |
199 | INIT_LIST_HEAD(&cache->object_list); | |
200 | INIT_LIST_HEAD(&cache->op_gc_list); | |
201 | spin_lock_init(&cache->object_list_lock); | |
202 | spin_lock_init(&cache->op_gc_list_lock); | |
203 | } | |
204 | EXPORT_SYMBOL(fscache_init_cache); | |
205 | ||
206 | /** | |
207 | * fscache_add_cache - Declare a cache as being open for business | |
208 | * @cache: The record describing the cache | |
209 | * @ifsdef: The record of the cache object describing the top-level index | |
210 | * @tagname: The tag describing this cache | |
211 | * | |
212 | * Add a cache to the system, making it available for netfs's to use. | |
213 | * | |
214 | * See Documentation/filesystems/caching/backend-api.txt for a complete | |
215 | * description. | |
216 | */ | |
217 | int fscache_add_cache(struct fscache_cache *cache, | |
218 | struct fscache_object *ifsdef, | |
219 | const char *tagname) | |
220 | { | |
221 | struct fscache_cache_tag *tag; | |
222 | ||
f29507ce | 223 | ASSERTCMP(ifsdef->cookie, ==, &fscache_fsdef_index); |
4c515dd4 DH |
224 | BUG_ON(!cache->ops); |
225 | BUG_ON(!ifsdef); | |
226 | ||
227 | cache->flags = 0; | |
caaef690 DH |
228 | ifsdef->event_mask = |
229 | ((1 << NR_FSCACHE_OBJECT_EVENTS) - 1) & | |
230 | ~(1 << FSCACHE_OBJECT_EV_CLEARED); | |
231 | __set_bit(FSCACHE_OBJECT_IS_AVAILABLE, &ifsdef->flags); | |
4c515dd4 DH |
232 | |
233 | if (!tagname) | |
234 | tagname = cache->identifier; | |
235 | ||
236 | BUG_ON(!tagname[0]); | |
237 | ||
238 | _enter("{%s.%s},,%s", cache->ops->name, cache->identifier, tagname); | |
239 | ||
240 | /* we use the cache tag to uniquely identify caches */ | |
241 | tag = __fscache_lookup_cache_tag(tagname); | |
242 | if (IS_ERR(tag)) | |
243 | goto nomem; | |
244 | ||
245 | if (test_and_set_bit(FSCACHE_TAG_RESERVED, &tag->flags)) | |
246 | goto tag_in_use; | |
247 | ||
248 | cache->kobj = kobject_create_and_add(tagname, fscache_root); | |
249 | if (!cache->kobj) | |
250 | goto error; | |
251 | ||
4c515dd4 DH |
252 | ifsdef->cache = cache; |
253 | cache->fsdef = ifsdef; | |
254 | ||
255 | down_write(&fscache_addremove_sem); | |
256 | ||
257 | tag->cache = cache; | |
258 | cache->tag = tag; | |
259 | ||
260 | /* add the cache to the list */ | |
261 | list_add(&cache->link, &fscache_cache_list); | |
262 | ||
263 | /* add the cache's netfs definition index object to the cache's | |
264 | * list */ | |
265 | spin_lock(&cache->object_list_lock); | |
266 | list_add_tail(&ifsdef->cache_link, &cache->object_list); | |
267 | spin_unlock(&cache->object_list_lock); | |
4fbf4291 | 268 | fscache_objlist_add(ifsdef); |
4c515dd4 DH |
269 | |
270 | /* add the cache's netfs definition index object to the top level index | |
271 | * cookie as a known backing object */ | |
272 | spin_lock(&fscache_fsdef_index.lock); | |
273 | ||
274 | hlist_add_head(&ifsdef->cookie_link, | |
275 | &fscache_fsdef_index.backing_objects); | |
276 | ||
277 | atomic_inc(&fscache_fsdef_index.usage); | |
278 | ||
279 | /* done */ | |
280 | spin_unlock(&fscache_fsdef_index.lock); | |
281 | up_write(&fscache_addremove_sem); | |
282 | ||
36dfd116 FF |
283 | pr_notice("Cache \"%s\" added (type %s)\n", |
284 | cache->tag->name, cache->ops->name); | |
4c515dd4 DH |
285 | kobject_uevent(cache->kobj, KOBJ_ADD); |
286 | ||
287 | _leave(" = 0 [%s]", cache->identifier); | |
288 | return 0; | |
289 | ||
290 | tag_in_use: | |
36dfd116 | 291 | pr_err("Cache tag '%s' already in use\n", tagname); |
4c515dd4 DH |
292 | __fscache_release_cache_tag(tag); |
293 | _leave(" = -EXIST"); | |
294 | return -EEXIST; | |
295 | ||
296 | error: | |
297 | __fscache_release_cache_tag(tag); | |
298 | _leave(" = -EINVAL"); | |
299 | return -EINVAL; | |
300 | ||
301 | nomem: | |
302 | _leave(" = -ENOMEM"); | |
303 | return -ENOMEM; | |
304 | } | |
305 | EXPORT_SYMBOL(fscache_add_cache); | |
306 | ||
307 | /** | |
308 | * fscache_io_error - Note a cache I/O error | |
309 | * @cache: The record describing the cache | |
310 | * | |
311 | * Note that an I/O error occurred in a cache and that it should no longer be | |
312 | * used for anything. This also reports the error into the kernel log. | |
313 | * | |
314 | * See Documentation/filesystems/caching/backend-api.txt for a complete | |
315 | * description. | |
316 | */ | |
317 | void fscache_io_error(struct fscache_cache *cache) | |
318 | { | |
75bc4113 | 319 | if (!test_and_set_bit(FSCACHE_IOERROR, &cache->flags)) |
36dfd116 | 320 | pr_err("Cache '%s' stopped due to I/O error\n", |
75bc4113 | 321 | cache->ops->name); |
4c515dd4 DH |
322 | } |
323 | EXPORT_SYMBOL(fscache_io_error); | |
324 | ||
325 | /* | |
326 | * request withdrawal of all the objects in a cache | |
327 | * - all the objects being withdrawn are moved onto the supplied list | |
328 | */ | |
329 | static void fscache_withdraw_all_objects(struct fscache_cache *cache, | |
330 | struct list_head *dying_objects) | |
331 | { | |
332 | struct fscache_object *object; | |
333 | ||
4c515dd4 | 334 | while (!list_empty(&cache->object_list)) { |
caaef690 | 335 | spin_lock(&cache->object_list_lock); |
4c515dd4 | 336 | |
caaef690 DH |
337 | if (!list_empty(&cache->object_list)) { |
338 | object = list_entry(cache->object_list.next, | |
339 | struct fscache_object, cache_link); | |
340 | list_move_tail(&object->cache_link, dying_objects); | |
4c515dd4 | 341 | |
caaef690 DH |
342 | _debug("withdraw %p", object->cookie); |
343 | ||
344 | /* This must be done under object_list_lock to prevent | |
345 | * a race with fscache_drop_object(). | |
346 | */ | |
347 | fscache_raise_event(object, FSCACHE_OBJECT_EV_KILL); | |
348 | } | |
4c515dd4 | 349 | |
caaef690 | 350 | spin_unlock(&cache->object_list_lock); |
4c515dd4 | 351 | cond_resched(); |
4c515dd4 | 352 | } |
4c515dd4 DH |
353 | } |
354 | ||
355 | /** | |
356 | * fscache_withdraw_cache - Withdraw a cache from the active service | |
357 | * @cache: The record describing the cache | |
358 | * | |
359 | * Withdraw a cache from service, unbinding all its cache objects from the | |
360 | * netfs cookies they're currently representing. | |
361 | * | |
362 | * See Documentation/filesystems/caching/backend-api.txt for a complete | |
363 | * description. | |
364 | */ | |
365 | void fscache_withdraw_cache(struct fscache_cache *cache) | |
366 | { | |
367 | LIST_HEAD(dying_objects); | |
368 | ||
369 | _enter(""); | |
370 | ||
36dfd116 FF |
371 | pr_notice("Withdrawing cache \"%s\"\n", |
372 | cache->tag->name); | |
4c515dd4 DH |
373 | |
374 | /* make the cache unavailable for cookie acquisition */ | |
375 | if (test_and_set_bit(FSCACHE_CACHE_WITHDRAWN, &cache->flags)) | |
376 | BUG(); | |
377 | ||
378 | down_write(&fscache_addremove_sem); | |
379 | list_del_init(&cache->link); | |
380 | cache->tag->cache = NULL; | |
381 | up_write(&fscache_addremove_sem); | |
382 | ||
383 | /* make sure all pages pinned by operations on behalf of the netfs are | |
384 | * written to disk */ | |
52bd75fd | 385 | fscache_stat(&fscache_n_cop_sync_cache); |
4c515dd4 | 386 | cache->ops->sync_cache(cache); |
52bd75fd | 387 | fscache_stat_d(&fscache_n_cop_sync_cache); |
4c515dd4 DH |
388 | |
389 | /* dissociate all the netfs pages backed by this cache from the block | |
390 | * mappings in the cache */ | |
52bd75fd | 391 | fscache_stat(&fscache_n_cop_dissociate_pages); |
4c515dd4 | 392 | cache->ops->dissociate_pages(cache); |
52bd75fd | 393 | fscache_stat_d(&fscache_n_cop_dissociate_pages); |
4c515dd4 DH |
394 | |
395 | /* we now have to destroy all the active objects pertaining to this | |
396 | * cache - which we do by passing them off to thread pool to be | |
397 | * disposed of */ | |
398 | _debug("destroy"); | |
399 | ||
400 | fscache_withdraw_all_objects(cache, &dying_objects); | |
401 | ||
402 | /* wait for all extant objects to finish their outstanding operations | |
403 | * and go away */ | |
404 | _debug("wait for finish"); | |
405 | wait_event(fscache_cache_cleared_wq, | |
406 | atomic_read(&cache->object_count) == 0); | |
407 | _debug("wait for clearance"); | |
408 | wait_event(fscache_cache_cleared_wq, | |
409 | list_empty(&cache->object_list)); | |
410 | _debug("cleared"); | |
411 | ASSERT(list_empty(&dying_objects)); | |
412 | ||
413 | kobject_put(cache->kobj); | |
414 | ||
415 | clear_bit(FSCACHE_TAG_RESERVED, &cache->tag->flags); | |
416 | fscache_release_cache_tag(cache->tag); | |
417 | cache->tag = NULL; | |
418 | ||
419 | _leave(""); | |
420 | } | |
421 | EXPORT_SYMBOL(fscache_withdraw_cache); |