diff options
Diffstat (limited to 'fs/cachefiles/interface.c')
-rw-r--r-- | fs/cachefiles/interface.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c new file mode 100644 index 000000000000..1e962348d111 --- /dev/null +++ b/fs/cachefiles/interface.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* FS-Cache interface to CacheFiles | ||
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 Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/mount.h> | ||
13 | #include <linux/buffer_head.h> | ||
14 | #include "internal.h" | ||
15 | |||
16 | #define list_to_page(head) (list_entry((head)->prev, struct page, lru)) | ||
17 | |||
18 | struct cachefiles_lookup_data { | ||
19 | struct cachefiles_xattr *auxdata; /* auxiliary data */ | ||
20 | char *key; /* key path */ | ||
21 | }; | ||
22 | |||
23 | static int cachefiles_attr_changed(struct fscache_object *_object); | ||
24 | |||
25 | /* | ||
26 | * allocate an object record for a cookie lookup and prepare the lookup data | ||
27 | */ | ||
28 | static struct fscache_object *cachefiles_alloc_object( | ||
29 | struct fscache_cache *_cache, | ||
30 | struct fscache_cookie *cookie) | ||
31 | { | ||
32 | struct cachefiles_lookup_data *lookup_data; | ||
33 | struct cachefiles_object *object; | ||
34 | struct cachefiles_cache *cache; | ||
35 | struct cachefiles_xattr *auxdata; | ||
36 | unsigned keylen, auxlen; | ||
37 | void *buffer; | ||
38 | char *key; | ||
39 | |||
40 | cache = container_of(_cache, struct cachefiles_cache, cache); | ||
41 | |||
42 | _enter("{%s},%p,", cache->cache.identifier, cookie); | ||
43 | |||
44 | lookup_data = kmalloc(sizeof(*lookup_data), GFP_KERNEL); | ||
45 | if (!lookup_data) | ||
46 | goto nomem_lookup_data; | ||
47 | |||
48 | /* create a new object record and a temporary leaf image */ | ||
49 | object = kmem_cache_alloc(cachefiles_object_jar, GFP_KERNEL); | ||
50 | if (!object) | ||
51 | goto nomem_object; | ||
52 | |||
53 | ASSERTCMP(object->backer, ==, NULL); | ||
54 | |||
55 | BUG_ON(test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); | ||
56 | atomic_set(&object->usage, 1); | ||
57 | |||
58 | fscache_object_init(&object->fscache, cookie, &cache->cache); | ||
59 | |||
60 | object->type = cookie->def->type; | ||
61 | |||
62 | /* get hold of the raw key | ||
63 | * - stick the length on the front and leave space on the back for the | ||
64 | * encoder | ||
65 | */ | ||
66 | buffer = kmalloc((2 + 512) + 3, GFP_KERNEL); | ||
67 | if (!buffer) | ||
68 | goto nomem_buffer; | ||
69 | |||
70 | keylen = cookie->def->get_key(cookie->netfs_data, buffer + 2, 512); | ||
71 | ASSERTCMP(keylen, <, 512); | ||
72 | |||
73 | *(uint16_t *)buffer = keylen; | ||
74 | ((char *)buffer)[keylen + 2] = 0; | ||
75 | ((char *)buffer)[keylen + 3] = 0; | ||
76 | ((char *)buffer)[keylen + 4] = 0; | ||
77 | |||
78 | /* turn the raw key into something that can work with as a filename */ | ||
79 | key = cachefiles_cook_key(buffer, keylen + 2, object->type); | ||
80 | if (!key) | ||
81 | goto nomem_key; | ||
82 | |||
83 | /* get hold of the auxiliary data and prepend the object type */ | ||
84 | auxdata = buffer; | ||
85 | auxlen = 0; | ||
86 | if (cookie->def->get_aux) { | ||
87 | auxlen = cookie->def->get_aux(cookie->netfs_data, | ||
88 | auxdata->data, 511); | ||
89 | ASSERTCMP(auxlen, <, 511); | ||
90 | } | ||
91 | |||
92 | auxdata->len = auxlen + 1; | ||
93 | auxdata->type = cookie->def->type; | ||
94 | |||
95 | lookup_data->auxdata = auxdata; | ||
96 | lookup_data->key = key; | ||
97 | object->lookup_data = lookup_data; | ||
98 | |||
99 | _leave(" = %p [%p]", &object->fscache, lookup_data); | ||
100 | return &object->fscache; | ||
101 | |||
102 | nomem_key: | ||
103 | kfree(buffer); | ||
104 | nomem_buffer: | ||
105 | BUG_ON(test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); | ||
106 | kmem_cache_free(cachefiles_object_jar, object); | ||
107 | fscache_object_destroyed(&cache->cache); | ||
108 | nomem_object: | ||
109 | kfree(lookup_data); | ||
110 | nomem_lookup_data: | ||
111 | _leave(" = -ENOMEM"); | ||
112 | return ERR_PTR(-ENOMEM); | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * attempt to look up the nominated node in this cache | ||
117 | */ | ||
118 | static void cachefiles_lookup_object(struct fscache_object *_object) | ||
119 | { | ||
120 | struct cachefiles_lookup_data *lookup_data; | ||
121 | struct cachefiles_object *parent, *object; | ||
122 | struct cachefiles_cache *cache; | ||
123 | const struct cred *saved_cred; | ||
124 | int ret; | ||
125 | |||
126 | _enter("{OBJ%x}", _object->debug_id); | ||
127 | |||
128 | cache = container_of(_object->cache, struct cachefiles_cache, cache); | ||
129 | parent = container_of(_object->parent, | ||
130 | struct cachefiles_object, fscache); | ||
131 | object = container_of(_object, struct cachefiles_object, fscache); | ||
132 | lookup_data = object->lookup_data; | ||
133 | |||
134 | ASSERTCMP(lookup_data, !=, NULL); | ||
135 | |||
136 | /* look up the key, creating any missing bits */ | ||
137 | cachefiles_begin_secure(cache, &saved_cred); | ||
138 | ret = cachefiles_walk_to_object(parent, object, | ||
139 | lookup_data->key, | ||
140 | lookup_data->auxdata); | ||
141 | cachefiles_end_secure(cache, saved_cred); | ||
142 | |||
143 | /* polish off by setting the attributes of non-index files */ | ||
144 | if (ret == 0 && | ||
145 | object->fscache.cookie->def->type != FSCACHE_COOKIE_TYPE_INDEX) | ||
146 | cachefiles_attr_changed(&object->fscache); | ||
147 | |||
148 | if (ret < 0) { | ||
149 | printk(KERN_WARNING "CacheFiles: Lookup failed error %d\n", | ||
150 | ret); | ||
151 | fscache_object_lookup_error(&object->fscache); | ||
152 | } | ||
153 | |||
154 | _leave(" [%d]", ret); | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * indication of lookup completion | ||
159 | */ | ||
160 | static void cachefiles_lookup_complete(struct fscache_object *_object) | ||
161 | { | ||
162 | struct cachefiles_object *object; | ||
163 | |||
164 | object = container_of(_object, struct cachefiles_object, fscache); | ||
165 | |||
166 | _enter("{OBJ%x,%p}", object->fscache.debug_id, object->lookup_data); | ||
167 | |||
168 | if (object->lookup_data) { | ||
169 | kfree(object->lookup_data->key); | ||
170 | kfree(object->lookup_data->auxdata); | ||
171 | kfree(object->lookup_data); | ||
172 | object->lookup_data = NULL; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * increment the usage count on an inode object (may fail if unmounting) | ||
178 | */ | ||
179 | static | ||
180 | struct fscache_object *cachefiles_grab_object(struct fscache_object *_object) | ||
181 | { | ||
182 | struct cachefiles_object *object = | ||
183 | container_of(_object, struct cachefiles_object, fscache); | ||
184 | |||
185 | _enter("{OBJ%x,%d}", _object->debug_id, atomic_read(&object->usage)); | ||
186 | |||
187 | #ifdef CACHEFILES_DEBUG_SLAB | ||
188 | ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000); | ||
189 | #endif | ||
190 | |||
191 | atomic_inc(&object->usage); | ||
192 | return &object->fscache; | ||
193 | } | ||
194 | |||
195 | /* | ||
196 | * update the auxilliary data for an object object on disk | ||
197 | */ | ||
198 | static void cachefiles_update_object(struct fscache_object *_object) | ||
199 | { | ||
200 | struct cachefiles_object *object; | ||
201 | struct cachefiles_xattr *auxdata; | ||
202 | struct cachefiles_cache *cache; | ||
203 | struct fscache_cookie *cookie; | ||
204 | const struct cred *saved_cred; | ||
205 | unsigned auxlen; | ||
206 | |||
207 | _enter("{OBJ%x}", _object->debug_id); | ||
208 | |||
209 | object = container_of(_object, struct cachefiles_object, fscache); | ||
210 | cache = container_of(object->fscache.cache, struct cachefiles_cache, | ||
211 | cache); | ||
212 | cookie = object->fscache.cookie; | ||
213 | |||
214 | if (!cookie->def->get_aux) { | ||
215 | _leave(" [no aux]"); | ||
216 | return; | ||
217 | } | ||
218 | |||
219 | auxdata = kmalloc(2 + 512 + 3, GFP_KERNEL); | ||
220 | if (!auxdata) { | ||
221 | _leave(" [nomem]"); | ||
222 | return; | ||
223 | } | ||
224 | |||
225 | auxlen = cookie->def->get_aux(cookie->netfs_data, auxdata->data, 511); | ||
226 | ASSERTCMP(auxlen, <, 511); | ||
227 | |||
228 | auxdata->len = auxlen + 1; | ||
229 | auxdata->type = cookie->def->type; | ||
230 | |||
231 | cachefiles_begin_secure(cache, &saved_cred); | ||
232 | cachefiles_update_object_xattr(object, auxdata); | ||
233 | cachefiles_end_secure(cache, saved_cred); | ||
234 | kfree(auxdata); | ||
235 | _leave(""); | ||
236 | } | ||
237 | |||
238 | /* | ||
239 | * discard the resources pinned by an object and effect retirement if | ||
240 | * requested | ||
241 | */ | ||
242 | static void cachefiles_drop_object(struct fscache_object *_object) | ||
243 | { | ||
244 | struct cachefiles_object *object; | ||
245 | struct cachefiles_cache *cache; | ||
246 | const struct cred *saved_cred; | ||
247 | |||
248 | ASSERT(_object); | ||
249 | |||
250 | object = container_of(_object, struct cachefiles_object, fscache); | ||
251 | |||
252 | _enter("{OBJ%x,%d}", | ||
253 | object->fscache.debug_id, atomic_read(&object->usage)); | ||
254 | |||
255 | cache = container_of(object->fscache.cache, | ||
256 | struct cachefiles_cache, cache); | ||
257 | |||
258 | #ifdef CACHEFILES_DEBUG_SLAB | ||
259 | ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000); | ||
260 | #endif | ||
261 | |||
262 | /* delete retired objects */ | ||
263 | if (object->fscache.state == FSCACHE_OBJECT_RECYCLING && | ||
264 | _object != cache->cache.fsdef | ||
265 | ) { | ||
266 | _debug("- retire object OBJ%x", object->fscache.debug_id); | ||
267 | cachefiles_begin_secure(cache, &saved_cred); | ||
268 | cachefiles_delete_object(cache, object); | ||
269 | cachefiles_end_secure(cache, saved_cred); | ||
270 | } | ||
271 | |||
272 | /* close the filesystem stuff attached to the object */ | ||
273 | if (object->backer != object->dentry) | ||
274 | dput(object->backer); | ||
275 | object->backer = NULL; | ||
276 | |||
277 | /* note that the object is now inactive */ | ||
278 | if (test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)) { | ||
279 | write_lock(&cache->active_lock); | ||
280 | if (!test_and_clear_bit(CACHEFILES_OBJECT_ACTIVE, | ||
281 | &object->flags)) | ||
282 | BUG(); | ||
283 | rb_erase(&object->active_node, &cache->active_nodes); | ||
284 | wake_up_bit(&object->flags, CACHEFILES_OBJECT_ACTIVE); | ||
285 | write_unlock(&cache->active_lock); | ||
286 | } | ||
287 | |||
288 | dput(object->dentry); | ||
289 | object->dentry = NULL; | ||
290 | |||
291 | _leave(""); | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * dispose of a reference to an object | ||
296 | */ | ||
297 | static void cachefiles_put_object(struct fscache_object *_object) | ||
298 | { | ||
299 | struct cachefiles_object *object; | ||
300 | struct fscache_cache *cache; | ||
301 | |||
302 | ASSERT(_object); | ||
303 | |||
304 | object = container_of(_object, struct cachefiles_object, fscache); | ||
305 | |||
306 | _enter("{OBJ%x,%d}", | ||
307 | object->fscache.debug_id, atomic_read(&object->usage)); | ||
308 | |||
309 | #ifdef CACHEFILES_DEBUG_SLAB | ||
310 | ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000); | ||
311 | #endif | ||
312 | |||
313 | ASSERTIFCMP(object->fscache.parent, | ||
314 | object->fscache.parent->n_children, >, 0); | ||
315 | |||
316 | if (atomic_dec_and_test(&object->usage)) { | ||
317 | _debug("- kill object OBJ%x", object->fscache.debug_id); | ||
318 | |||
319 | ASSERT(!test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); | ||
320 | ASSERTCMP(object->fscache.parent, ==, NULL); | ||
321 | ASSERTCMP(object->backer, ==, NULL); | ||
322 | ASSERTCMP(object->dentry, ==, NULL); | ||
323 | ASSERTCMP(object->fscache.n_ops, ==, 0); | ||
324 | ASSERTCMP(object->fscache.n_children, ==, 0); | ||
325 | |||
326 | if (object->lookup_data) { | ||
327 | kfree(object->lookup_data->key); | ||
328 | kfree(object->lookup_data->auxdata); | ||
329 | kfree(object->lookup_data); | ||
330 | object->lookup_data = NULL; | ||
331 | } | ||
332 | |||
333 | cache = object->fscache.cache; | ||
334 | kmem_cache_free(cachefiles_object_jar, object); | ||
335 | fscache_object_destroyed(cache); | ||
336 | } | ||
337 | |||
338 | _leave(""); | ||
339 | } | ||
340 | |||
341 | /* | ||
342 | * sync a cache | ||
343 | */ | ||
344 | static void cachefiles_sync_cache(struct fscache_cache *_cache) | ||
345 | { | ||
346 | struct cachefiles_cache *cache; | ||
347 | const struct cred *saved_cred; | ||
348 | int ret; | ||
349 | |||
350 | _enter("%p", _cache); | ||
351 | |||
352 | cache = container_of(_cache, struct cachefiles_cache, cache); | ||
353 | |||
354 | /* make sure all pages pinned by operations on behalf of the netfs are | ||
355 | * written to disc */ | ||
356 | cachefiles_begin_secure(cache, &saved_cred); | ||
357 | ret = fsync_super(cache->mnt->mnt_sb); | ||
358 | cachefiles_end_secure(cache, saved_cred); | ||
359 | |||
360 | if (ret == -EIO) | ||
361 | cachefiles_io_error(cache, | ||
362 | "Attempt to sync backing fs superblock" | ||
363 | " returned error %d", | ||
364 | ret); | ||
365 | } | ||
366 | |||
367 | /* | ||
368 | * notification the attributes on an object have changed | ||
369 | * - called with reads/writes excluded by FS-Cache | ||
370 | */ | ||
371 | static int cachefiles_attr_changed(struct fscache_object *_object) | ||
372 | { | ||
373 | struct cachefiles_object *object; | ||
374 | struct cachefiles_cache *cache; | ||
375 | const struct cred *saved_cred; | ||
376 | struct iattr newattrs; | ||
377 | uint64_t ni_size; | ||
378 | loff_t oi_size; | ||
379 | int ret; | ||
380 | |||
381 | _object->cookie->def->get_attr(_object->cookie->netfs_data, &ni_size); | ||
382 | |||
383 | _enter("{OBJ%x},[%llu]", | ||
384 | _object->debug_id, (unsigned long long) ni_size); | ||
385 | |||
386 | object = container_of(_object, struct cachefiles_object, fscache); | ||
387 | cache = container_of(object->fscache.cache, | ||
388 | struct cachefiles_cache, cache); | ||
389 | |||
390 | if (ni_size == object->i_size) | ||
391 | return 0; | ||
392 | |||
393 | if (!object->backer) | ||
394 | return -ENOBUFS; | ||
395 | |||
396 | ASSERT(S_ISREG(object->backer->d_inode->i_mode)); | ||
397 | |||
398 | fscache_set_store_limit(&object->fscache, ni_size); | ||
399 | |||
400 | oi_size = i_size_read(object->backer->d_inode); | ||
401 | if (oi_size == ni_size) | ||
402 | return 0; | ||
403 | |||
404 | newattrs.ia_size = ni_size; | ||
405 | newattrs.ia_valid = ATTR_SIZE; | ||
406 | |||
407 | cachefiles_begin_secure(cache, &saved_cred); | ||
408 | mutex_lock(&object->backer->d_inode->i_mutex); | ||
409 | ret = notify_change(object->backer, &newattrs); | ||
410 | mutex_unlock(&object->backer->d_inode->i_mutex); | ||
411 | cachefiles_end_secure(cache, saved_cred); | ||
412 | |||
413 | if (ret == -EIO) { | ||
414 | fscache_set_store_limit(&object->fscache, 0); | ||
415 | cachefiles_io_error_obj(object, "Size set failed"); | ||
416 | ret = -ENOBUFS; | ||
417 | } | ||
418 | |||
419 | _leave(" = %d", ret); | ||
420 | return ret; | ||
421 | } | ||
422 | |||
423 | /* | ||
424 | * dissociate a cache from all the pages it was backing | ||
425 | */ | ||
426 | static void cachefiles_dissociate_pages(struct fscache_cache *cache) | ||
427 | { | ||
428 | _enter(""); | ||
429 | } | ||
430 | |||
431 | const struct fscache_cache_ops cachefiles_cache_ops = { | ||
432 | .name = "cachefiles", | ||
433 | .alloc_object = cachefiles_alloc_object, | ||
434 | .lookup_object = cachefiles_lookup_object, | ||
435 | .lookup_complete = cachefiles_lookup_complete, | ||
436 | .grab_object = cachefiles_grab_object, | ||
437 | .update_object = cachefiles_update_object, | ||
438 | .drop_object = cachefiles_drop_object, | ||
439 | .put_object = cachefiles_put_object, | ||
440 | .sync_cache = cachefiles_sync_cache, | ||
441 | .attr_changed = cachefiles_attr_changed, | ||
442 | .read_or_alloc_page = cachefiles_read_or_alloc_page, | ||
443 | .read_or_alloc_pages = cachefiles_read_or_alloc_pages, | ||
444 | .allocate_page = cachefiles_allocate_page, | ||
445 | .allocate_pages = cachefiles_allocate_pages, | ||
446 | .write_page = cachefiles_write_page, | ||
447 | .uncache_page = cachefiles_uncache_page, | ||
448 | .dissociate_pages = cachefiles_dissociate_pages, | ||
449 | }; | ||