aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cachefiles/rdwr.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cachefiles/rdwr.c')
-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()) {