aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2009-11-19 13:12:05 -0500
committerDavid Howells <dhowells@redhat.com>2009-11-19 13:12:05 -0500
commitfee096deb4f33897937b974cb2c5168bab7935be (patch)
treec86e5ed5b3435ff0f0266f343b19f8cc7be63340 /fs
parentd0e27b7808dc667f3015be0b6888f6d680e222c8 (diff)
CacheFiles: Catch an overly long wait for an old active object
Catch an overly long wait for an old, dying active object when we want to replace it with a new one. The probability is that all the slow-work threads are hogged, and the delete can't get a look in. What we do instead is: (1) if there's nothing in the slow work queue, we sleep until either the dying object has finished dying or there is something in the slow work queue behind which we can queue our object. (2) if there is something in the slow work queue, we return ETIMEDOUT to fscache_lookup_object(), which then puts us back on the slow work queue, presumably behind the deletion that we're blocked by. We are then deferred for a while until we work our way back through the queue - without blocking a slow-work thread unnecessarily. A backtrace similar to the following may appear in the log without this patch: INFO: task kslowd004:5711 blocked for more than 120 seconds. "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. kslowd004 D 0000000000000000 0 5711 2 0x00000080 ffff88000340bb80 0000000000000046 ffff88002550d000 0000000000000000 ffff88002550d000 0000000000000007 ffff88000340bfd8 ffff88002550d2a8 000000000000ddf0 00000000000118c0 00000000000118c0 ffff88002550d2a8 Call Trace: [<ffffffff81058e21>] ? trace_hardirqs_on+0xd/0xf [<ffffffffa011c4d8>] ? cachefiles_wait_bit+0x0/0xd [cachefiles] [<ffffffffa011c4e1>] cachefiles_wait_bit+0x9/0xd [cachefiles] [<ffffffff81353153>] __wait_on_bit+0x43/0x76 [<ffffffff8111ae39>] ? ext3_xattr_get+0x1ec/0x270 [<ffffffff813531ef>] out_of_line_wait_on_bit+0x69/0x74 [<ffffffffa011c4d8>] ? cachefiles_wait_bit+0x0/0xd [cachefiles] [<ffffffff8104c125>] ? wake_bit_function+0x0/0x2e [<ffffffffa011bc79>] cachefiles_mark_object_active+0x203/0x23b [cachefiles] [<ffffffffa011c209>] cachefiles_walk_to_object+0x558/0x827 [cachefiles] [<ffffffffa011a429>] cachefiles_lookup_object+0xac/0x12a [cachefiles] [<ffffffffa00aa1e9>] fscache_lookup_object+0x1c7/0x214 [fscache] [<ffffffffa00aafc5>] fscache_object_state_machine+0xa5/0x52d [fscache] [<ffffffffa00ab4ac>] fscache_object_slow_work_execute+0x5f/0xa0 [fscache] [<ffffffff81082093>] slow_work_execute+0x18f/0x2d1 [<ffffffff8108239a>] slow_work_thread+0x1c5/0x308 [<ffffffff8104c0f1>] ? autoremove_wake_function+0x0/0x34 [<ffffffff810821d5>] ? slow_work_thread+0x0/0x308 [<ffffffff8104be91>] kthread+0x7a/0x82 [<ffffffff8100beda>] child_rip+0xa/0x20 [<ffffffff8100b87c>] ? restore_args+0x0/0x30 [<ffffffff8104be17>] ? kthread+0x0/0x82 [<ffffffff8100bed0>] ? child_rip+0x0/0x20 1 lock held by kslowd004/5711: #0: (&sb->s_type->i_mutex_key#7/1){+.+.+.}, at: [<ffffffffa011be64>] cachefiles_walk_to_object+0x1b3/0x827 [cachefiles] Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/cachefiles/interface.c6
-rw-r--r--fs/cachefiles/namei.c87
-rw-r--r--fs/fscache/internal.h1
-rw-r--r--fs/fscache/object.c10
-rw-r--r--fs/fscache/stats.c4
5 files changed, 85 insertions, 23 deletions
diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c
index 8e67abf05985..9d3c426044ae 100644
--- a/fs/cachefiles/interface.c
+++ b/fs/cachefiles/interface.c
@@ -114,8 +114,9 @@ nomem_lookup_data:
114 114
115/* 115/*
116 * attempt to look up the nominated node in this cache 116 * attempt to look up the nominated node in this cache
117 * - return -ETIMEDOUT to be scheduled again
117 */ 118 */
118static void cachefiles_lookup_object(struct fscache_object *_object) 119static int cachefiles_lookup_object(struct fscache_object *_object)
119{ 120{
120 struct cachefiles_lookup_data *lookup_data; 121 struct cachefiles_lookup_data *lookup_data;
121 struct cachefiles_object *parent, *object; 122 struct cachefiles_object *parent, *object;
@@ -145,13 +146,14 @@ static void cachefiles_lookup_object(struct fscache_object *_object)
145 object->fscache.cookie->def->type != FSCACHE_COOKIE_TYPE_INDEX) 146 object->fscache.cookie->def->type != FSCACHE_COOKIE_TYPE_INDEX)
146 cachefiles_attr_changed(&object->fscache); 147 cachefiles_attr_changed(&object->fscache);
147 148
148 if (ret < 0) { 149 if (ret < 0 && ret != -ETIMEDOUT) {
149 printk(KERN_WARNING "CacheFiles: Lookup failed error %d\n", 150 printk(KERN_WARNING "CacheFiles: Lookup failed error %d\n",
150 ret); 151 ret);
151 fscache_object_lookup_error(&object->fscache); 152 fscache_object_lookup_error(&object->fscache);
152 } 153 }
153 154
154 _leave(" [%d]", ret); 155 _leave(" [%d]", ret);
156 return ret;
155} 157}
156 158
157/* 159/*
diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
index 00a0cda8f47a..14ac4806e291 100644
--- a/fs/cachefiles/namei.c
+++ b/fs/cachefiles/namei.c
@@ -21,12 +21,6 @@
21#include <linux/security.h> 21#include <linux/security.h>
22#include "internal.h" 22#include "internal.h"
23 23
24static int cachefiles_wait_bit(void *flags)
25{
26 schedule();
27 return 0;
28}
29
30#define CACHEFILES_KEYBUF_SIZE 512 24#define CACHEFILES_KEYBUF_SIZE 512
31 25
32/* 26/*
@@ -100,8 +94,8 @@ static noinline void cachefiles_printk_object(struct cachefiles_object *object,
100/* 94/*
101 * record the fact that an object is now active 95 * record the fact that an object is now active
102 */ 96 */
103static void cachefiles_mark_object_active(struct cachefiles_cache *cache, 97static int cachefiles_mark_object_active(struct cachefiles_cache *cache,
104 struct cachefiles_object *object) 98 struct cachefiles_object *object)
105{ 99{
106 struct cachefiles_object *xobject; 100 struct cachefiles_object *xobject;
107 struct rb_node **_p, *_parent = NULL; 101 struct rb_node **_p, *_parent = NULL;
@@ -139,8 +133,8 @@ try_again:
139 rb_insert_color(&object->active_node, &cache->active_nodes); 133 rb_insert_color(&object->active_node, &cache->active_nodes);
140 134
141 write_unlock(&cache->active_lock); 135 write_unlock(&cache->active_lock);
142 _leave(""); 136 _leave(" = 0");
143 return; 137 return 0;
144 138
145 /* an old object from a previous incarnation is hogging the slot - we 139 /* an old object from a previous incarnation is hogging the slot - we
146 * need to wait for it to be destroyed */ 140 * need to wait for it to be destroyed */
@@ -155,13 +149,64 @@ wait_for_old_object:
155 atomic_inc(&xobject->usage); 149 atomic_inc(&xobject->usage);
156 write_unlock(&cache->active_lock); 150 write_unlock(&cache->active_lock);
157 151
158 _debug(">>> wait"); 152 if (test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags)) {
159 wait_on_bit(&xobject->flags, CACHEFILES_OBJECT_ACTIVE, 153 wait_queue_head_t *wq;
160 cachefiles_wait_bit, TASK_UNINTERRUPTIBLE); 154
161 _debug("<<< waited"); 155 signed long timeout = 60 * HZ;
156 wait_queue_t wait;
157 bool requeue;
158
159 /* if the object we're waiting for is queued for processing,
160 * then just put ourselves on the queue behind it */
161 if (slow_work_is_queued(&xobject->fscache.work)) {
162 _debug("queue OBJ%x behind OBJ%x immediately",
163 object->fscache.debug_id,
164 xobject->fscache.debug_id);
165 goto requeue;
166 }
167
168 /* otherwise we sleep until either the object we're waiting for
169 * is done, or the slow-work facility wants the thread back to
170 * do other work */
171 wq = bit_waitqueue(&xobject->flags, CACHEFILES_OBJECT_ACTIVE);
172 init_wait(&wait);
173 requeue = false;
174 do {
175 prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE);
176 if (!test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags))
177 break;
178 requeue = slow_work_sleep_till_thread_needed(
179 &object->fscache.work, &timeout);
180 } while (timeout > 0 && !requeue);
181 finish_wait(wq, &wait);
182
183 if (requeue &&
184 test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags)) {
185 _debug("queue OBJ%x behind OBJ%x after wait",
186 object->fscache.debug_id,
187 xobject->fscache.debug_id);
188 goto requeue;
189 }
190
191 if (timeout <= 0) {
192 printk(KERN_ERR "\n");
193 printk(KERN_ERR "CacheFiles: Error: Overlong"
194 " wait for old active object to go away\n");
195 cachefiles_printk_object(object, xobject);
196 goto requeue;
197 }
198 }
199
200 ASSERT(!test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags));
162 201
163 cache->cache.ops->put_object(&xobject->fscache); 202 cache->cache.ops->put_object(&xobject->fscache);
164 goto try_again; 203 goto try_again;
204
205requeue:
206 clear_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags);
207 cache->cache.ops->put_object(&xobject->fscache);
208 _leave(" = -ETIMEDOUT");
209 return -ETIMEDOUT;
165} 210}
166 211
167/* 212/*
@@ -466,12 +511,15 @@ lookup_again:
466 } 511 }
467 512
468 /* note that we're now using this object */ 513 /* note that we're now using this object */
469 cachefiles_mark_object_active(cache, object); 514 ret = cachefiles_mark_object_active(cache, object);
470 515
471 mutex_unlock(&dir->d_inode->i_mutex); 516 mutex_unlock(&dir->d_inode->i_mutex);
472 dput(dir); 517 dput(dir);
473 dir = NULL; 518 dir = NULL;
474 519
520 if (ret == -ETIMEDOUT)
521 goto mark_active_timed_out;
522
475 _debug("=== OBTAINED_OBJECT ==="); 523 _debug("=== OBTAINED_OBJECT ===");
476 524
477 if (object->new) { 525 if (object->new) {
@@ -515,6 +563,10 @@ create_error:
515 cachefiles_io_error(cache, "Create/mkdir failed"); 563 cachefiles_io_error(cache, "Create/mkdir failed");
516 goto error; 564 goto error;
517 565
566mark_active_timed_out:
567 _debug("mark active timed out");
568 goto release_dentry;
569
518check_error: 570check_error:
519 _debug("check error %d", ret); 571 _debug("check error %d", ret);
520 write_lock(&cache->active_lock); 572 write_lock(&cache->active_lock);
@@ -522,7 +574,7 @@ check_error:
522 clear_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags); 574 clear_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags);
523 wake_up_bit(&object->flags, CACHEFILES_OBJECT_ACTIVE); 575 wake_up_bit(&object->flags, CACHEFILES_OBJECT_ACTIVE);
524 write_unlock(&cache->active_lock); 576 write_unlock(&cache->active_lock);
525 577release_dentry:
526 dput(object->dentry); 578 dput(object->dentry);
527 object->dentry = NULL; 579 object->dentry = NULL;
528 goto error_out; 580 goto error_out;
@@ -543,9 +595,6 @@ error:
543error_out2: 595error_out2:
544 dput(dir); 596 dput(dir);
545error_out: 597error_out:
546 if (ret == -ENOSPC)
547 ret = -ENOBUFS;
548
549 _leave(" = error %d", -ret); 598 _leave(" = error %d", -ret);
550 return ret; 599 return ret;
551} 600}
diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h
index 5b49a373689b..0ca2566e038c 100644
--- a/fs/fscache/internal.h
+++ b/fs/fscache/internal.h
@@ -215,6 +215,7 @@ extern atomic_t fscache_n_object_no_alloc;
215extern atomic_t fscache_n_object_lookups; 215extern atomic_t fscache_n_object_lookups;
216extern atomic_t fscache_n_object_lookups_negative; 216extern atomic_t fscache_n_object_lookups_negative;
217extern atomic_t fscache_n_object_lookups_positive; 217extern atomic_t fscache_n_object_lookups_positive;
218extern atomic_t fscache_n_object_lookups_timed_out;
218extern atomic_t fscache_n_object_created; 219extern atomic_t fscache_n_object_created;
219extern atomic_t fscache_n_object_avail; 220extern atomic_t fscache_n_object_avail;
220extern atomic_t fscache_n_object_dead; 221extern atomic_t fscache_n_object_dead;
diff --git a/fs/fscache/object.c b/fs/fscache/object.c
index f3f952cf887e..e513ac599c8e 100644
--- a/fs/fscache/object.c
+++ b/fs/fscache/object.c
@@ -468,6 +468,7 @@ static void fscache_lookup_object(struct fscache_object *object)
468{ 468{
469 struct fscache_cookie *cookie = object->cookie; 469 struct fscache_cookie *cookie = object->cookie;
470 struct fscache_object *parent; 470 struct fscache_object *parent;
471 int ret;
471 472
472 _enter(""); 473 _enter("");
473 474
@@ -493,12 +494,19 @@ static void fscache_lookup_object(struct fscache_object *object)
493 494
494 fscache_stat(&fscache_n_object_lookups); 495 fscache_stat(&fscache_n_object_lookups);
495 fscache_stat(&fscache_n_cop_lookup_object); 496 fscache_stat(&fscache_n_cop_lookup_object);
496 object->cache->ops->lookup_object(object); 497 ret = object->cache->ops->lookup_object(object);
497 fscache_stat_d(&fscache_n_cop_lookup_object); 498 fscache_stat_d(&fscache_n_cop_lookup_object);
498 499
499 if (test_bit(FSCACHE_OBJECT_EV_ERROR, &object->events)) 500 if (test_bit(FSCACHE_OBJECT_EV_ERROR, &object->events))
500 set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); 501 set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags);
501 502
503 if (ret == -ETIMEDOUT) {
504 /* probably stuck behind another object, so move this one to
505 * the back of the queue */
506 fscache_stat(&fscache_n_object_lookups_timed_out);
507 set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events);
508 }
509
502 _leave(""); 510 _leave("");
503} 511}
504 512
diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c
index 05f77caf4a2d..46435f3aae68 100644
--- a/fs/fscache/stats.c
+++ b/fs/fscache/stats.c
@@ -98,6 +98,7 @@ atomic_t fscache_n_object_no_alloc;
98atomic_t fscache_n_object_lookups; 98atomic_t fscache_n_object_lookups;
99atomic_t fscache_n_object_lookups_negative; 99atomic_t fscache_n_object_lookups_negative;
100atomic_t fscache_n_object_lookups_positive; 100atomic_t fscache_n_object_lookups_positive;
101atomic_t fscache_n_object_lookups_timed_out;
101atomic_t fscache_n_object_created; 102atomic_t fscache_n_object_created;
102atomic_t fscache_n_object_avail; 103atomic_t fscache_n_object_avail;
103atomic_t fscache_n_object_dead; 104atomic_t fscache_n_object_dead;
@@ -160,10 +161,11 @@ static int fscache_stats_show(struct seq_file *m, void *v)
160 atomic_read(&fscache_n_acquires_nobufs), 161 atomic_read(&fscache_n_acquires_nobufs),
161 atomic_read(&fscache_n_acquires_oom)); 162 atomic_read(&fscache_n_acquires_oom));
162 163
163 seq_printf(m, "Lookups: n=%u neg=%u pos=%u crt=%u\n", 164 seq_printf(m, "Lookups: n=%u neg=%u pos=%u crt=%u tmo=%u\n",
164 atomic_read(&fscache_n_object_lookups), 165 atomic_read(&fscache_n_object_lookups),
165 atomic_read(&fscache_n_object_lookups_negative), 166 atomic_read(&fscache_n_object_lookups_negative),
166 atomic_read(&fscache_n_object_lookups_positive), 167 atomic_read(&fscache_n_object_lookups_positive),
168 atomic_read(&fscache_n_object_lookups_timed_out),
167 atomic_read(&fscache_n_object_created)); 169 atomic_read(&fscache_n_object_created));
168 170
169 seq_printf(m, "Updates: n=%u nul=%u run=%u\n", 171 seq_printf(m, "Updates: n=%u nul=%u run=%u\n",