diff options
author | David Howells <dhowells@redhat.com> | 2012-12-05 08:34:48 -0500 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2012-12-20 17:10:58 -0500 |
commit | 8d76349d359064859217dc292dc8733e209705af (patch) | |
tree | 07a43fff286f2c911d14a8714bf3dcb3321f5dfd /fs/fscache | |
parent | 75bc411388f4aeb9fb0381bd56eb5d67193ed9a1 (diff) |
FS-Cache: Exclusive op submission can BUG if there's been an I/O error
The function to submit an exclusive op (fscache_submit_exclusive_op()) can BUG
if there's been an I/O error because it may see the parent cache object in an
unexpected state. It should only BUG if there hasn't been an I/O error.
In this case the problem was produced by remounting the cache partition to be
R/O. The EROFS state was detected and the cache was aborted, but not
everything handled the aborting correctly.
SysRq : Emergency Remount R/O
EXT4-fs (sda6): re-mounted. Opts: (null)
Emergency Remount complete
CacheFiles: I/O Error: Failed to update xattr with error -30
FS-Cache: Cache cachefiles stopped due to I/O error
------------[ cut here ]------------
kernel BUG at fs/fscache/operation.c:128!
invalid opcode: 0000 [#1] SMP
CPU 0
Modules linked in: cachefiles nfs fscache auth_rpcgss nfs_acl lockd sunrpc
Pid: 6612, comm: kworker/u:2 Not tainted 3.1.0-rc8-fsdevel+ #1093 /DG965RY
RIP: 0010:[<ffffffffa00739c0>] [<ffffffffa00739c0>] fscache_submit_exclusive_op+0x2ad/0x2c2 [fscache]
RSP: 0018:ffff880000853d40 EFLAGS: 00010206
RAX: ffff880038ac72a8 RBX: ffff8800181f2260 RCX: ffffffff81f2b2b0
RDX: 0000000000000001 RSI: ffffffff8179a478 RDI: ffff8800181f2280
RBP: ffff880000853d60 R08: 0000000000000002 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000001 R12: ffff880038ac7268
R13: ffff8800181f2280 R14: ffff88003a359190 R15: 000000010122b162
FS: 0000000000000000(0000) GS:ffff88003bc00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 00000034cc4a77f0 CR3: 0000000010e96000 CR4: 00000000000006f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Process kworker/u:2 (pid: 6612, threadinfo ffff880000852000, task ffff880014c3c040)
Stack:
ffff8800181f2260 ffff8800181f2310 ffff880038ac7268 ffff8800181f2260
ffff880000853dc0 ffffffffa0072375 ffff880037ecfe00 ffff88003a359198
ffff880000853dc0 0000000000000246 0000000000000000 ffff88000a91d308
Call Trace:
[<ffffffffa0072375>] fscache_object_work_func+0x792/0xe65 [fscache]
[<ffffffff81047e44>] process_one_work+0x1eb/0x37f
[<ffffffff81047de6>] ? process_one_work+0x18d/0x37f
[<ffffffffa0071be3>] ? fscache_enqueue_dependents+0xd8/0xd8 [fscache]
[<ffffffff810482e4>] worker_thread+0x15a/0x21a
[<ffffffff8104818a>] ? rescuer_thread+0x188/0x188
[<ffffffff8104bf96>] kthread+0x7f/0x87
[<ffffffff813ad6f4>] kernel_thread_helper+0x4/0x10
[<ffffffff81026b98>] ? finish_task_switch+0x45/0xc0
[<ffffffff813abd1d>] ? retint_restore_args+0xe/0xe
[<ffffffff8104bf17>] ? __init_kthread_worker+0x53/0x53
[<ffffffff813ad6f0>] ? gs_change+0xb/0xb
Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs/fscache')
-rw-r--r-- | fs/fscache/internal.h | 1 | ||||
-rw-r--r-- | fs/fscache/object.c | 23 | ||||
-rw-r--r-- | fs/fscache/operation.c | 13 |
3 files changed, 28 insertions, 9 deletions
diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index c81179303930..dcb3e1d5dbf6 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h | |||
@@ -288,6 +288,7 @@ extern const struct file_operations fscache_stats_fops; | |||
288 | static inline void fscache_raise_event(struct fscache_object *object, | 288 | static inline void fscache_raise_event(struct fscache_object *object, |
289 | unsigned event) | 289 | unsigned event) |
290 | { | 290 | { |
291 | BUG_ON(event >= NR_FSCACHE_OBJECT_EVENTS); | ||
291 | if (!test_and_set_bit(event, &object->events) && | 292 | if (!test_and_set_bit(event, &object->events) && |
292 | test_bit(event, &object->event_mask)) | 293 | test_bit(event, &object->event_mask)) |
293 | fscache_enqueue_object(object); | 294 | fscache_enqueue_object(object); |
diff --git a/fs/fscache/object.c b/fs/fscache/object.c index 2ef8a082a272..2c512cbac380 100644 --- a/fs/fscache/object.c +++ b/fs/fscache/object.c | |||
@@ -103,6 +103,7 @@ static void fscache_object_state_machine(struct fscache_object *object) | |||
103 | { | 103 | { |
104 | enum fscache_object_state new_state; | 104 | enum fscache_object_state new_state; |
105 | struct fscache_cookie *cookie; | 105 | struct fscache_cookie *cookie; |
106 | int event; | ||
106 | 107 | ||
107 | ASSERT(object != NULL); | 108 | ASSERT(object != NULL); |
108 | 109 | ||
@@ -275,7 +276,8 @@ static void fscache_object_state_machine(struct fscache_object *object) | |||
275 | 276 | ||
276 | /* determine the transition from a lookup state */ | 277 | /* determine the transition from a lookup state */ |
277 | lookup_transit: | 278 | lookup_transit: |
278 | switch (fls(object->events & object->event_mask) - 1) { | 279 | event = fls(object->events & object->event_mask) - 1; |
280 | switch (event) { | ||
279 | case FSCACHE_OBJECT_EV_WITHDRAW: | 281 | case FSCACHE_OBJECT_EV_WITHDRAW: |
280 | case FSCACHE_OBJECT_EV_RETIRE: | 282 | case FSCACHE_OBJECT_EV_RETIRE: |
281 | case FSCACHE_OBJECT_EV_RELEASE: | 283 | case FSCACHE_OBJECT_EV_RELEASE: |
@@ -292,7 +294,8 @@ lookup_transit: | |||
292 | 294 | ||
293 | /* determine the transition from an active state */ | 295 | /* determine the transition from an active state */ |
294 | active_transit: | 296 | active_transit: |
295 | switch (fls(object->events & object->event_mask) - 1) { | 297 | event = fls(object->events & object->event_mask) - 1; |
298 | switch (event) { | ||
296 | case FSCACHE_OBJECT_EV_WITHDRAW: | 299 | case FSCACHE_OBJECT_EV_WITHDRAW: |
297 | case FSCACHE_OBJECT_EV_RETIRE: | 300 | case FSCACHE_OBJECT_EV_RETIRE: |
298 | case FSCACHE_OBJECT_EV_RELEASE: | 301 | case FSCACHE_OBJECT_EV_RELEASE: |
@@ -314,7 +317,8 @@ active_transit: | |||
314 | 317 | ||
315 | /* determine the transition from a terminal state */ | 318 | /* determine the transition from a terminal state */ |
316 | terminal_transit: | 319 | terminal_transit: |
317 | switch (fls(object->events & object->event_mask) - 1) { | 320 | event = fls(object->events & object->event_mask) - 1; |
321 | switch (event) { | ||
318 | case FSCACHE_OBJECT_EV_WITHDRAW: | 322 | case FSCACHE_OBJECT_EV_WITHDRAW: |
319 | new_state = FSCACHE_OBJECT_WITHDRAWING; | 323 | new_state = FSCACHE_OBJECT_WITHDRAWING; |
320 | goto change_state; | 324 | goto change_state; |
@@ -347,8 +351,8 @@ done: | |||
347 | 351 | ||
348 | unsupported_event: | 352 | unsupported_event: |
349 | printk(KERN_ERR "FS-Cache:" | 353 | printk(KERN_ERR "FS-Cache:" |
350 | " Unsupported event %lx [mask %lx] in state %s\n", | 354 | " Unsupported event %d [%lx/%lx] in state %s\n", |
351 | object->events, object->event_mask, | 355 | event, object->events, object->event_mask, |
352 | fscache_object_states[object->state]); | 356 | fscache_object_states[object->state]); |
353 | BUG(); | 357 | BUG(); |
354 | } | 358 | } |
@@ -945,7 +949,7 @@ static void fscache_invalidate_object(struct fscache_object *object) | |||
945 | 949 | ||
946 | spin_lock(&cookie->lock); | 950 | spin_lock(&cookie->lock); |
947 | if (fscache_submit_exclusive_op(object, op) < 0) | 951 | if (fscache_submit_exclusive_op(object, op) < 0) |
948 | BUG(); | 952 | goto submit_op_failed; |
949 | spin_unlock(&cookie->lock); | 953 | spin_unlock(&cookie->lock); |
950 | fscache_put_operation(op); | 954 | fscache_put_operation(op); |
951 | 955 | ||
@@ -960,4 +964,11 @@ static void fscache_invalidate_object(struct fscache_object *object) | |||
960 | */ | 964 | */ |
961 | fscache_invalidation_complete(cookie); | 965 | fscache_invalidation_complete(cookie); |
962 | _leave(""); | 966 | _leave(""); |
967 | return; | ||
968 | |||
969 | submit_op_failed: | ||
970 | spin_unlock(&cookie->lock); | ||
971 | kfree(op); | ||
972 | fscache_raise_event(object, FSCACHE_OBJECT_EV_ERROR); | ||
973 | _leave(" [EIO]"); | ||
963 | } | 974 | } |
diff --git a/fs/fscache/operation.c b/fs/fscache/operation.c index c58dbe613266..9e6b7d232bb1 100644 --- a/fs/fscache/operation.c +++ b/fs/fscache/operation.c | |||
@@ -84,6 +84,8 @@ static void fscache_run_op(struct fscache_object *object, | |||
84 | int fscache_submit_exclusive_op(struct fscache_object *object, | 84 | int fscache_submit_exclusive_op(struct fscache_object *object, |
85 | struct fscache_operation *op) | 85 | struct fscache_operation *op) |
86 | { | 86 | { |
87 | int ret; | ||
88 | |||
87 | _enter("{OBJ%x OP%x},", object->debug_id, op->debug_id); | 89 | _enter("{OBJ%x OP%x},", object->debug_id, op->debug_id); |
88 | 90 | ||
89 | ASSERTCMP(op->state, ==, FSCACHE_OP_ST_INITIALISED); | 91 | ASSERTCMP(op->state, ==, FSCACHE_OP_ST_INITIALISED); |
@@ -116,6 +118,7 @@ int fscache_submit_exclusive_op(struct fscache_object *object, | |||
116 | 118 | ||
117 | /* need to issue a new write op after this */ | 119 | /* need to issue a new write op after this */ |
118 | clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); | 120 | clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); |
121 | ret = 0; | ||
119 | } else if (object->state == FSCACHE_OBJECT_CREATING) { | 122 | } else if (object->state == FSCACHE_OBJECT_CREATING) { |
120 | op->object = object; | 123 | op->object = object; |
121 | object->n_ops++; | 124 | object->n_ops++; |
@@ -123,13 +126,17 @@ int fscache_submit_exclusive_op(struct fscache_object *object, | |||
123 | atomic_inc(&op->usage); | 126 | atomic_inc(&op->usage); |
124 | list_add_tail(&op->pend_link, &object->pending_ops); | 127 | list_add_tail(&op->pend_link, &object->pending_ops); |
125 | fscache_stat(&fscache_n_op_pend); | 128 | fscache_stat(&fscache_n_op_pend); |
129 | ret = 0; | ||
126 | } else { | 130 | } else { |
127 | /* not allowed to submit ops in any other state */ | 131 | /* If we're in any other state, there must have been an I/O |
128 | BUG(); | 132 | * error of some nature. |
133 | */ | ||
134 | ASSERT(test_bit(FSCACHE_IOERROR, &object->cache->flags)); | ||
135 | ret = -EIO; | ||
129 | } | 136 | } |
130 | 137 | ||
131 | spin_unlock(&object->lock); | 138 | spin_unlock(&object->lock); |
132 | return 0; | 139 | return ret; |
133 | } | 140 | } |
134 | 141 | ||
135 | /* | 142 | /* |