mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
7af46292da
commit
8f54c7a4ba
|
@ -775,3 +775,13 @@ static inline bool nfs_error_is_fatal(int err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool nfs_error_is_fatal_on_server(int err)
|
||||||
|
{
|
||||||
|
switch (err) {
|
||||||
|
case 0:
|
||||||
|
case -ERESTARTSYS:
|
||||||
|
case -EINTR:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return nfs_error_is_fatal(err);
|
||||||
|
}
|
||||||
|
|
|
@ -91,19 +91,25 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
|
EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
|
||||||
|
|
||||||
static void nfs_readpage_release(struct nfs_page *req)
|
static void nfs_readpage_release(struct nfs_page *req, int error)
|
||||||
{
|
{
|
||||||
struct inode *inode = d_inode(nfs_req_openctx(req)->dentry);
|
struct inode *inode = d_inode(nfs_req_openctx(req)->dentry);
|
||||||
|
struct page *page = req->wb_page;
|
||||||
|
|
||||||
dprintk("NFS: read done (%s/%llu %d@%lld)\n", inode->i_sb->s_id,
|
dprintk("NFS: read done (%s/%llu %d@%lld)\n", inode->i_sb->s_id,
|
||||||
(unsigned long long)NFS_FILEID(inode), req->wb_bytes,
|
(unsigned long long)NFS_FILEID(inode), req->wb_bytes,
|
||||||
(long long)req_offset(req));
|
(long long)req_offset(req));
|
||||||
|
|
||||||
|
if (nfs_error_is_fatal_on_server(error) && error != -ETIMEDOUT)
|
||||||
|
SetPageError(page);
|
||||||
if (nfs_page_group_sync_on_bit(req, PG_UNLOCKPAGE)) {
|
if (nfs_page_group_sync_on_bit(req, PG_UNLOCKPAGE)) {
|
||||||
if (PageUptodate(req->wb_page))
|
struct address_space *mapping = page_file_mapping(page);
|
||||||
nfs_readpage_to_fscache(inode, req->wb_page, 0);
|
|
||||||
|
|
||||||
unlock_page(req->wb_page);
|
if (PageUptodate(page))
|
||||||
|
nfs_readpage_to_fscache(inode, page, 0);
|
||||||
|
else if (!PageError(page) && !PagePrivate(page))
|
||||||
|
generic_error_remove_page(mapping, page);
|
||||||
|
unlock_page(page);
|
||||||
}
|
}
|
||||||
nfs_release_request(req);
|
nfs_release_request(req);
|
||||||
}
|
}
|
||||||
|
@ -131,7 +137,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
|
||||||
&nfs_async_read_completion_ops);
|
&nfs_async_read_completion_ops);
|
||||||
if (!nfs_pageio_add_request(&pgio, new)) {
|
if (!nfs_pageio_add_request(&pgio, new)) {
|
||||||
nfs_list_remove_request(new);
|
nfs_list_remove_request(new);
|
||||||
nfs_readpage_release(new);
|
nfs_readpage_release(new, pgio.pg_error);
|
||||||
}
|
}
|
||||||
nfs_pageio_complete(&pgio);
|
nfs_pageio_complete(&pgio);
|
||||||
|
|
||||||
|
@ -153,6 +159,7 @@ static void nfs_page_group_set_uptodate(struct nfs_page *req)
|
||||||
static void nfs_read_completion(struct nfs_pgio_header *hdr)
|
static void nfs_read_completion(struct nfs_pgio_header *hdr)
|
||||||
{
|
{
|
||||||
unsigned long bytes = 0;
|
unsigned long bytes = 0;
|
||||||
|
int error;
|
||||||
|
|
||||||
if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
|
if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -179,14 +186,19 @@ static void nfs_read_completion(struct nfs_pgio_header *hdr)
|
||||||
zero_user_segment(page, start, end);
|
zero_user_segment(page, start, end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
error = 0;
|
||||||
bytes += req->wb_bytes;
|
bytes += req->wb_bytes;
|
||||||
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) {
|
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) {
|
||||||
if (bytes <= hdr->good_bytes)
|
if (bytes <= hdr->good_bytes)
|
||||||
nfs_page_group_set_uptodate(req);
|
nfs_page_group_set_uptodate(req);
|
||||||
|
else {
|
||||||
|
error = hdr->error;
|
||||||
|
xchg(&nfs_req_openctx(req)->error, error);
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
nfs_page_group_set_uptodate(req);
|
nfs_page_group_set_uptodate(req);
|
||||||
nfs_list_remove_request(req);
|
nfs_list_remove_request(req);
|
||||||
nfs_readpage_release(req);
|
nfs_readpage_release(req, error);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
hdr->release(hdr);
|
hdr->release(hdr);
|
||||||
|
@ -213,7 +225,7 @@ nfs_async_read_error(struct list_head *head, int error)
|
||||||
while (!list_empty(head)) {
|
while (!list_empty(head)) {
|
||||||
req = nfs_list_entry(head->next);
|
req = nfs_list_entry(head->next);
|
||||||
nfs_list_remove_request(req);
|
nfs_list_remove_request(req);
|
||||||
nfs_readpage_release(req);
|
nfs_readpage_release(req, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,8 +349,13 @@ int nfs_readpage(struct file *file, struct page *page)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xchg(&ctx->error, 0);
|
||||||
error = nfs_readpage_async(ctx, inode, page);
|
error = nfs_readpage_async(ctx, inode, page);
|
||||||
|
if (!error) {
|
||||||
|
error = wait_on_page_locked_killable(page);
|
||||||
|
if (!PageUptodate(page) && !error)
|
||||||
|
error = xchg(&ctx->error, 0);
|
||||||
|
}
|
||||||
out:
|
out:
|
||||||
put_nfs_open_context(ctx);
|
put_nfs_open_context(ctx);
|
||||||
return error;
|
return error;
|
||||||
|
@ -372,8 +389,8 @@ readpage_async_filler(void *data, struct page *page)
|
||||||
zero_user_segment(page, len, PAGE_SIZE);
|
zero_user_segment(page, len, PAGE_SIZE);
|
||||||
if (!nfs_pageio_add_request(desc->pgio, new)) {
|
if (!nfs_pageio_add_request(desc->pgio, new)) {
|
||||||
nfs_list_remove_request(new);
|
nfs_list_remove_request(new);
|
||||||
nfs_readpage_release(new);
|
|
||||||
error = desc->pgio->pg_error;
|
error = desc->pgio->pg_error;
|
||||||
|
nfs_readpage_release(new, error);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -599,18 +599,6 @@ static void nfs_write_error(struct nfs_page *req, int error)
|
||||||
nfs_release_request(req);
|
nfs_release_request(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
nfs_error_is_fatal_on_server(int err)
|
|
||||||
{
|
|
||||||
switch (err) {
|
|
||||||
case 0:
|
|
||||||
case -ERESTARTSYS:
|
|
||||||
case -EINTR:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return nfs_error_is_fatal(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find an associated nfs write request, and prepare to flush it out
|
* Find an associated nfs write request, and prepare to flush it out
|
||||||
* May return an error if the user signalled nfs_wait_on_request().
|
* May return an error if the user signalled nfs_wait_on_request().
|
||||||
|
|
Loading…
Reference in New Issue