diff options
author | David Howells <dhowells@redhat.com> | 2014-09-30 09:50:28 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2014-09-30 09:50:28 -0400 |
commit | a3b7c00484e1177e7eb9b047c46cac571b82442f (patch) | |
tree | af896c176c43d334691e5853104fe7bc126c4842 /fs/cachefiles | |
parent | fe82dcec644244676d55a1384c958d5f67979adb (diff) |
CacheFiles: Handle object being killed before being set up
If a cache object gets killed whilst in the process of being set up - for
instance if the netfs relinquishes the cookie that the object is associated
with - then the object's state machine will transit to the DROP_OBJECT state
without necessarily going through the LOOKUP_OBJECT or CREATE_OBJECT states.
This is a problem for CacheFiles because cachefiles_drop_object() assumes that
object->dentry will be set upon reaching the DROP_OBJECT state and has an
ASSERT() to that effect (see the oops below) - but object->dentry doesn't get
set until the LOOKUP_OBJECT or CREATE_OBJECT states (and not always then if
they fail).
To fix this, just make the dentry cleanup in cachefiles_drop_object()
conditional on the dentry actually being set and remove the assertion.
CacheFiles: Assertion failed
------------[ cut here ]------------
kernel BUG at .../fs/cachefiles/namei.c:425!
...
Workqueue: fscache_object fscache_object_work_func [fscache]
...
RIP: ... cachefiles_delete_object+0xcd/0x110 [cachefiles]
...
Call Trace:
[<ffffffffa043280f>] ? cachefiles_drop_object+0xff/0x130 [cachefiles]
[<ffffffffa02ac511>] ? fscache_drop_object+0xd1/0x1d0 [fscache]
[<ffffffffa02ac697>] ? fscache_object_work_func+0x87/0x210 [fscache]
[<ffffffff81080635>] ? process_one_work+0x155/0x450
[<ffffffff81081c44>] ? worker_thread+0x114/0x370
[<ffffffff81081b30>] ? manage_workers.isra.21+0x2c0/0x2c0
[<ffffffff81087fcc>] ? kthread+0xbc/0xe0
[<ffffffff81087f10>] ? flush_kthread_worker+0xa0/0xa0
[<ffffffff8150638c>] ? ret_from_fork+0x7c/0xb0
[<ffffffff81087f10>] ? flush_kthread_worker+0xa0/0xa0
Reported-by: Manuel Schölling <manuel.schoelling@gmx.de>
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Steve Dickson <steved@redhat.com>
Diffstat (limited to 'fs/cachefiles')
-rw-r--r-- | fs/cachefiles/interface.c | 33 |
1 files changed, 20 insertions, 13 deletions
diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index 584743d456c3..1c7293c3a93a 100644 --- a/fs/cachefiles/interface.c +++ b/fs/cachefiles/interface.c | |||
@@ -268,20 +268,27 @@ static void cachefiles_drop_object(struct fscache_object *_object) | |||
268 | ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000); | 268 | ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000); |
269 | #endif | 269 | #endif |
270 | 270 | ||
271 | /* delete retired objects */ | 271 | /* We need to tidy the object up if we did in fact manage to open it. |
272 | if (test_bit(FSCACHE_OBJECT_RETIRED, &object->fscache.flags) && | 272 | * It's possible for us to get here before the object is fully |
273 | _object != cache->cache.fsdef | 273 | * initialised if the parent goes away or the object gets retired |
274 | ) { | 274 | * before we set it up. |
275 | _debug("- retire object OBJ%x", object->fscache.debug_id); | 275 | */ |
276 | cachefiles_begin_secure(cache, &saved_cred); | 276 | if (object->dentry) { |
277 | cachefiles_delete_object(cache, object); | 277 | /* delete retired objects */ |
278 | cachefiles_end_secure(cache, saved_cred); | 278 | if (test_bit(FSCACHE_OBJECT_RETIRED, &object->fscache.flags) && |
279 | } | 279 | _object != cache->cache.fsdef |
280 | ) { | ||
281 | _debug("- retire object OBJ%x", object->fscache.debug_id); | ||
282 | cachefiles_begin_secure(cache, &saved_cred); | ||
283 | cachefiles_delete_object(cache, object); | ||
284 | cachefiles_end_secure(cache, saved_cred); | ||
285 | } | ||
280 | 286 | ||
281 | /* close the filesystem stuff attached to the object */ | 287 | /* close the filesystem stuff attached to the object */ |
282 | if (object->backer != object->dentry) | 288 | if (object->backer != object->dentry) |
283 | dput(object->backer); | 289 | dput(object->backer); |
284 | object->backer = NULL; | 290 | object->backer = NULL; |
291 | } | ||
285 | 292 | ||
286 | /* note that the object is now inactive */ | 293 | /* note that the object is now inactive */ |
287 | if (test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)) { | 294 | if (test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)) { |