diff options
author | Trond Myklebust <trond.myklebust@hammerspace.com> | 2019-08-15 12:26:05 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@hammerspace.com> | 2019-08-26 15:31:29 -0400 |
commit | 8f54c7a4babf58bbaf849e126f7ae9664bdc9e04 (patch) | |
tree | 86afff853c994e30fa86df68064eb1e8578d4126 | |
parent | 7af46292dadcf8870946916f79fdddf79bd7267f (diff) |
NFS: Fix spurious EIO read errors
If the client attempts to read a page, but the read fails due to some
spurious error (e.g. an ACCESS error or a timeout, ...) then we need
to allow other processes to retry.
Also try to report errors correctly when doing a synchronous readpage.
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
-rw-r--r-- | fs/nfs/internal.h | 10 | ||||
-rw-r--r-- | fs/nfs/read.c | 35 | ||||
-rw-r--r-- | fs/nfs/write.c | 12 |
3 files changed, 36 insertions, 21 deletions
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index a2346a2f8361..e64f810223be 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -775,3 +775,13 @@ static inline bool nfs_error_is_fatal(int err) | |||
775 | } | 775 | } |
776 | } | 776 | } |
777 | 777 | ||
778 | static inline bool nfs_error_is_fatal_on_server(int err) | ||
779 | { | ||
780 | switch (err) { | ||
781 | case 0: | ||
782 | case -ERESTARTSYS: | ||
783 | case -EINTR: | ||
784 | return false; | ||
785 | } | ||
786 | return nfs_error_is_fatal(err); | ||
787 | } | ||
diff --git a/fs/nfs/read.c b/fs/nfs/read.c index c19841c82b6a..cfe0b586eadd 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c | |||
@@ -91,19 +91,25 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio) | |||
91 | } | 91 | } |
92 | EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds); | 92 | EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds); |
93 | 93 | ||
94 | static void nfs_readpage_release(struct nfs_page *req) | 94 | static void nfs_readpage_release(struct nfs_page *req, int error) |
95 | { | 95 | { |
96 | struct inode *inode = d_inode(nfs_req_openctx(req)->dentry); | 96 | struct inode *inode = d_inode(nfs_req_openctx(req)->dentry); |
97 | struct page *page = req->wb_page; | ||
97 | 98 | ||
98 | dprintk("NFS: read done (%s/%llu %d@%lld)\n", inode->i_sb->s_id, | 99 | dprintk("NFS: read done (%s/%llu %d@%lld)\n", inode->i_sb->s_id, |
99 | (unsigned long long)NFS_FILEID(inode), req->wb_bytes, | 100 | (unsigned long long)NFS_FILEID(inode), req->wb_bytes, |
100 | (long long)req_offset(req)); | 101 | (long long)req_offset(req)); |
101 | 102 | ||
103 | if (nfs_error_is_fatal_on_server(error) && error != -ETIMEDOUT) | ||
104 | SetPageError(page); | ||
102 | if (nfs_page_group_sync_on_bit(req, PG_UNLOCKPAGE)) { | 105 | if (nfs_page_group_sync_on_bit(req, PG_UNLOCKPAGE)) { |
103 | if (PageUptodate(req->wb_page)) | 106 | struct address_space *mapping = page_file_mapping(page); |
104 | nfs_readpage_to_fscache(inode, req->wb_page, 0); | ||
105 | 107 | ||
106 | unlock_page(req->wb_page); | 108 | if (PageUptodate(page)) |
109 | nfs_readpage_to_fscache(inode, page, 0); | ||
110 | else if (!PageError(page) && !PagePrivate(page)) | ||
111 | generic_error_remove_page(mapping, page); | ||
112 | unlock_page(page); | ||
107 | } | 113 | } |
108 | nfs_release_request(req); | 114 | nfs_release_request(req); |
109 | } | 115 | } |
@@ -131,7 +137,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, | |||
131 | &nfs_async_read_completion_ops); | 137 | &nfs_async_read_completion_ops); |
132 | if (!nfs_pageio_add_request(&pgio, new)) { | 138 | if (!nfs_pageio_add_request(&pgio, new)) { |
133 | nfs_list_remove_request(new); | 139 | nfs_list_remove_request(new); |
134 | nfs_readpage_release(new); | 140 | nfs_readpage_release(new, pgio.pg_error); |
135 | } | 141 | } |
136 | nfs_pageio_complete(&pgio); | 142 | nfs_pageio_complete(&pgio); |
137 | 143 | ||
@@ -153,6 +159,7 @@ static void nfs_page_group_set_uptodate(struct nfs_page *req) | |||
153 | static void nfs_read_completion(struct nfs_pgio_header *hdr) | 159 | static void nfs_read_completion(struct nfs_pgio_header *hdr) |
154 | { | 160 | { |
155 | unsigned long bytes = 0; | 161 | unsigned long bytes = 0; |
162 | int error; | ||
156 | 163 | ||
157 | if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) | 164 | if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) |
158 | goto out; | 165 | goto out; |
@@ -179,14 +186,19 @@ static void nfs_read_completion(struct nfs_pgio_header *hdr) | |||
179 | zero_user_segment(page, start, end); | 186 | zero_user_segment(page, start, end); |
180 | } | 187 | } |
181 | } | 188 | } |
189 | error = 0; | ||
182 | bytes += req->wb_bytes; | 190 | bytes += req->wb_bytes; |
183 | if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) { | 191 | if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) { |
184 | if (bytes <= hdr->good_bytes) | 192 | if (bytes <= hdr->good_bytes) |
185 | nfs_page_group_set_uptodate(req); | 193 | nfs_page_group_set_uptodate(req); |
194 | else { | ||
195 | error = hdr->error; | ||
196 | xchg(&nfs_req_openctx(req)->error, error); | ||
197 | } | ||
186 | } else | 198 | } else |
187 | nfs_page_group_set_uptodate(req); | 199 | nfs_page_group_set_uptodate(req); |
188 | nfs_list_remove_request(req); | 200 | nfs_list_remove_request(req); |
189 | nfs_readpage_release(req); | 201 | nfs_readpage_release(req, error); |
190 | } | 202 | } |
191 | out: | 203 | out: |
192 | hdr->release(hdr); | 204 | hdr->release(hdr); |
@@ -213,7 +225,7 @@ nfs_async_read_error(struct list_head *head, int error) | |||
213 | while (!list_empty(head)) { | 225 | while (!list_empty(head)) { |
214 | req = nfs_list_entry(head->next); | 226 | req = nfs_list_entry(head->next); |
215 | nfs_list_remove_request(req); | 227 | nfs_list_remove_request(req); |
216 | nfs_readpage_release(req); | 228 | nfs_readpage_release(req, error); |
217 | } | 229 | } |
218 | } | 230 | } |
219 | 231 | ||
@@ -337,8 +349,13 @@ int nfs_readpage(struct file *file, struct page *page) | |||
337 | goto out; | 349 | goto out; |
338 | } | 350 | } |
339 | 351 | ||
352 | xchg(&ctx->error, 0); | ||
340 | error = nfs_readpage_async(ctx, inode, page); | 353 | error = nfs_readpage_async(ctx, inode, page); |
341 | 354 | if (!error) { | |
355 | error = wait_on_page_locked_killable(page); | ||
356 | if (!PageUptodate(page) && !error) | ||
357 | error = xchg(&ctx->error, 0); | ||
358 | } | ||
342 | out: | 359 | out: |
343 | put_nfs_open_context(ctx); | 360 | put_nfs_open_context(ctx); |
344 | return error; | 361 | return error; |
@@ -372,8 +389,8 @@ readpage_async_filler(void *data, struct page *page) | |||
372 | zero_user_segment(page, len, PAGE_SIZE); | 389 | zero_user_segment(page, len, PAGE_SIZE); |
373 | if (!nfs_pageio_add_request(desc->pgio, new)) { | 390 | if (!nfs_pageio_add_request(desc->pgio, new)) { |
374 | nfs_list_remove_request(new); | 391 | nfs_list_remove_request(new); |
375 | nfs_readpage_release(new); | ||
376 | error = desc->pgio->pg_error; | 392 | error = desc->pgio->pg_error; |
393 | nfs_readpage_release(new, error); | ||
377 | goto out; | 394 | goto out; |
378 | } | 395 | } |
379 | return 0; | 396 | return 0; |
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 3399149435ce..cee9905e419c 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -599,18 +599,6 @@ static void nfs_write_error(struct nfs_page *req, int error) | |||
599 | nfs_release_request(req); | 599 | nfs_release_request(req); |
600 | } | 600 | } |
601 | 601 | ||
602 | static bool | ||
603 | nfs_error_is_fatal_on_server(int err) | ||
604 | { | ||
605 | switch (err) { | ||
606 | case 0: | ||
607 | case -ERESTARTSYS: | ||
608 | case -EINTR: | ||
609 | return false; | ||
610 | } | ||
611 | return nfs_error_is_fatal(err); | ||
612 | } | ||
613 | |||
614 | /* | 602 | /* |
615 | * Find an associated nfs write request, and prepare to flush it out | 603 | * Find an associated nfs write request, and prepare to flush it out |
616 | * May return an error if the user signalled nfs_wait_on_request(). | 604 | * May return an error if the user signalled nfs_wait_on_request(). |