diff options
Diffstat (limited to 'fs/cachefiles/rdwr.c')
-rw-r--r-- | fs/cachefiles/rdwr.c | 99 |
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 | */ | ||
72 | static 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 | |||
134 | unlock_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()) { |