aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2009-11-19 13:11:55 -0500
committerDavid Howells <dhowells@redhat.com>2009-11-19 13:11:55 -0500
commit5e929b33c3935ecb029b3e495356b2b8af432efa (patch)
tree99f892f4ea926d94b441856e27f1e08814ab1c75
parenta17754fb8c28af19cd70dcbec6d5b0773b94e0c1 (diff)
CacheFiles: Handle truncate unlocking the page we're reading
Handle truncate unlocking the page we're attempting to read from the backing device before the read has completed. This was causing reports like the following to occur: Pid: 4765, comm: kslowd Not tainted 2.6.30.1 #1 Call Trace: [<ffffffffa0331d7a>] ? cachefiles_read_waiter+0xd9/0x147 [cachefiles] [<ffffffff804b74bd>] ? __wait_on_bit+0x60/0x6f [<ffffffff8022bbbb>] ? __wake_up_common+0x3f/0x71 [<ffffffff8022cc32>] ? __wake_up+0x30/0x44 [<ffffffff8024a41f>] ? __wake_up_bit+0x28/0x2d [<ffffffffa003a793>] ? ext3_truncate+0x4d7/0x8ed [ext3] [<ffffffff80281f90>] ? pagevec_lookup+0x17/0x1f [<ffffffff8028c2ff>] ? unmap_mapping_range+0x59/0x1ff [<ffffffff8022cc32>] ? __wake_up+0x30/0x44 [<ffffffff8028e286>] ? vmtruncate+0xc2/0xe2 [<ffffffff802b82cf>] ? inode_setattr+0x22/0x10a [<ffffffffa003baa5>] ? ext3_setattr+0x17b/0x1e6 [ext3] [<ffffffff802b853d>] ? notify_change+0x186/0x2c9 [<ffffffffa032d9de>] ? cachefiles_attr_changed+0x133/0x1cd [cachefiles] [<ffffffffa032df7f>] ? cachefiles_lookup_object+0xcf/0x12a [cachefiles] [<ffffffffa0318165>] ? fscache_lookup_object+0x110/0x122 [fscache] [<ffffffffa03188c3>] ? fscache_object_slow_work_execute+0x590/0x6bc [fscache] [<ffffffff80278f82>] ? slow_work_thread+0x285/0x43a [<ffffffff8024a446>] ? autoremove_wake_function+0x0/0x2e [<ffffffff80278cfd>] ? slow_work_thread+0x0/0x43a [<ffffffff8024a317>] ? kthread+0x54/0x81 [<ffffffff8020c93a>] ? child_rip+0xa/0x20 [<ffffffff8024a2c3>] ? kthread+0x0/0x81 [<ffffffff8020c930>] ? child_rip+0x0/0x20 CacheFiles: I/O Error: Readpage failed on backing file 200000000000810 FS-Cache: Cache cachefiles stopped due to I/O error Reported-by: Christian Kujau <lists@nerdbynature.de> Reported-by: Takashi Iwai <tiwai@suse.de> Reported-by: Duc Le Minh <duclm.vn@gmail.com> Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r--fs/cachefiles/rdwr.c99
1 files changed, 93 insertions, 6 deletions
diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c
index 5a84fd7109ad..1d8332563863 100644
--- a/fs/cachefiles/rdwr.c
+++ b/fs/cachefiles/rdwr.c
@@ -40,8 +40,10 @@ static int cachefiles_read_waiter(wait_queue_t *wait, unsigned mode,
40 40
41 _debug("--- monitor %p %lx ---", page, page->flags); 41 _debug("--- monitor %p %lx ---", page, page->flags);
42 42
43 if (!PageUptodate(page) && !PageError(page)) 43 if (!PageUptodate(page) && !PageError(page)) {
44 dump_stack(); 44 /* unlocked, not uptodate and not erronous? */
45 _debug("page probably truncated");
46 }
45 47
46 /* remove from the waitqueue */ 48 /* remove from the waitqueue */
47 list_del(&wait->task_list); 49 list_del(&wait->task_list);
@@ -61,6 +63,84 @@ static int cachefiles_read_waiter(wait_queue_t *wait, unsigned mode,
61} 63}
62 64
63/* 65/*
66 * handle a probably truncated page
67 * - check to see if the page is still relevant and reissue the read if
68 * possible
69 * - return -EIO on error, -ENODATA if the page is gone, -EINPROGRESS if we
70 * must wait again and 0 if successful
71 */
72static int cachefiles_read_reissue(struct cachefiles_object *object,
73 struct cachefiles_one_read *monitor)
74{
75 struct address_space *bmapping = object->backer->d_inode->i_mapping;
76 struct page *backpage = monitor->back_page, *backpage2;
77 int ret;
78
79 kenter("{ino=%lx},{%lx,%lx}",
80 object->backer->d_inode->i_ino,
81 backpage->index, backpage->flags);
82
83 /* skip if the page was truncated away completely */
84 if (backpage->mapping != bmapping) {
85 kleave(" = -ENODATA [mapping]");
86 return -ENODATA;
87 }
88
89 backpage2 = find_get_page(bmapping, backpage->index);
90 if (!backpage2) {
91 kleave(" = -ENODATA [gone]");
92 return -ENODATA;
93 }
94
95 if (backpage != backpage2) {
96 put_page(backpage2);
97 kleave(" = -ENODATA [different]");
98 return -ENODATA;
99 }
100
101 /* the page is still there and we already have a ref on it, so we don't
102 * need a second */
103 put_page(backpage2);
104
105 INIT_LIST_HEAD(&monitor->op_link);
106 add_page_wait_queue(backpage, &monitor->monitor);
107
108 if (trylock_page(backpage)) {
109 ret = -EIO;
110 if (PageError(backpage))
111 goto unlock_discard;
112 ret = 0;
113 if (PageUptodate(backpage))
114 goto unlock_discard;
115
116 kdebug("reissue read");
117 ret = bmapping->a_ops->readpage(NULL, backpage);
118 if (ret < 0)
119 goto unlock_discard;
120 }
121
122 /* but the page may have been read before the monitor was installed, so
123 * the monitor may miss the event - so we have to ensure that we do get
124 * one in such a case */
125 if (trylock_page(backpage)) {
126 _debug("jumpstart %p {%lx}", backpage, backpage->flags);
127 unlock_page(backpage);
128 }
129
130 /* it'll reappear on the todo list */
131 kleave(" = -EINPROGRESS");
132 return -EINPROGRESS;
133
134unlock_discard:
135 unlock_page(backpage);
136 spin_lock_irq(&object->work_lock);
137 list_del(&monitor->op_link);
138 spin_unlock_irq(&object->work_lock);
139 kleave(" = %d", ret);
140 return ret;
141}
142
143/*
64 * copy data from backing pages to netfs pages to complete a read operation 144 * copy data from backing pages to netfs pages to complete a read operation
65 * - driven by FS-Cache's thread pool 145 * - driven by FS-Cache's thread pool
66 */ 146 */
@@ -92,20 +172,26 @@ static void cachefiles_read_copier(struct fscache_operation *_op)
92 172
93 _debug("- copy {%lu}", monitor->back_page->index); 173 _debug("- copy {%lu}", monitor->back_page->index);
94 174
95 error = -EIO; 175 recheck:
96 if (PageUptodate(monitor->back_page)) { 176 if (PageUptodate(monitor->back_page)) {
97 copy_highpage(monitor->netfs_page, monitor->back_page); 177 copy_highpage(monitor->netfs_page, monitor->back_page);
98 178
99 pagevec_add(&pagevec, monitor->netfs_page); 179 pagevec_add(&pagevec, monitor->netfs_page);
100 fscache_mark_pages_cached(monitor->op, &pagevec); 180 fscache_mark_pages_cached(monitor->op, &pagevec);
101 error = 0; 181 error = 0;
102 } 182 } else if (!PageError(monitor->back_page)) {
103 183 /* the page has probably been truncated */
104 if (error) 184 error = cachefiles_read_reissue(object, monitor);
185 if (error == -EINPROGRESS)
186 goto next;
187 goto recheck;
188 } else {
105 cachefiles_io_error_obj( 189 cachefiles_io_error_obj(
106 object, 190 object,
107 "Readpage failed on backing file %lx", 191 "Readpage failed on backing file %lx",
108 (unsigned long) monitor->back_page->flags); 192 (unsigned long) monitor->back_page->flags);
193 error = -EIO;
194 }
109 195
110 page_cache_release(monitor->back_page); 196 page_cache_release(monitor->back_page);
111 197
@@ -114,6 +200,7 @@ static void cachefiles_read_copier(struct fscache_operation *_op)
114 fscache_put_retrieval(op); 200 fscache_put_retrieval(op);
115 kfree(monitor); 201 kfree(monitor);
116 202
203 next:
117 /* let the thread pool have some air occasionally */ 204 /* let the thread pool have some air occasionally */
118 max--; 205 max--;
119 if (max < 0 || need_resched()) { 206 if (max < 0 || need_resched()) {