}
 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 page *page = req->wb_page;
 
        dprintk("NFS: read done (%s/%llu %d@%lld)\n", inode->i_sb->s_id,
                (unsigned long long)NFS_FILEID(inode), req->wb_bytes,
                (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 (PageUptodate(req->wb_page))
-                       nfs_readpage_to_fscache(inode, req->wb_page, 0);
+               struct address_space *mapping = page_file_mapping(page);
 
-               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_async_read_completion_ops);
        if (!nfs_pageio_add_request(&pgio, new)) {
                nfs_list_remove_request(new);
-               nfs_readpage_release(new);
+               nfs_readpage_release(new, pgio.pg_error);
        }
        nfs_pageio_complete(&pgio);
 
 static void nfs_read_completion(struct nfs_pgio_header *hdr)
 {
        unsigned long bytes = 0;
+       int error;
 
        if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
                goto out;
                                zero_user_segment(page, start, end);
                        }
                }
+               error = 0;
                bytes += req->wb_bytes;
                if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) {
                        if (bytes <= hdr->good_bytes)
                                nfs_page_group_set_uptodate(req);
+                       else {
+                               error = hdr->error;
+                               xchg(&nfs_req_openctx(req)->error, error);
+                       }
                } else
                        nfs_page_group_set_uptodate(req);
                nfs_list_remove_request(req);
-               nfs_readpage_release(req);
+               nfs_readpage_release(req, error);
        }
 out:
        hdr->release(hdr);
        while (!list_empty(head)) {
                req = nfs_list_entry(head->next);
                nfs_list_remove_request(req);
-               nfs_readpage_release(req);
+               nfs_readpage_release(req, error);
        }
 }
 
                        goto out;
        }
 
+       xchg(&ctx->error, 0);
        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:
        put_nfs_open_context(ctx);
        return error;
                zero_user_segment(page, len, PAGE_SIZE);
        if (!nfs_pageio_add_request(desc->pgio, new)) {
                nfs_list_remove_request(new);
-               nfs_readpage_release(new);
                error = desc->pgio->pg_error;
+               nfs_readpage_release(new, error);
                goto out;
        }
        return 0;