nfs: handle request add failure properly
authorPeng Tao <tao.peng@primarydata.com>
Sat, 5 Dec 2015 07:57:31 +0000 (15:57 +0800)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Mon, 28 Dec 2015 19:32:37 +0000 (14:32 -0500)
When we fail to queue a read page to IO descriptor,
we need to clean it up otherwise it is hanging around
preventing nfs module from being removed.

When we fail to queue a write page to IO descriptor,
we need to clean it up and also save the failure status
to open context. Then at file close, we can try to write
pages back again and drop the page if it fails to writeback
in .launder_page, which will be done in the next patch.

Signed-off-by: Peng Tao <tao.peng@primarydata.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/pnfs.c
fs/nfs/read.c
fs/nfs/write.c

index c7e8b87da5b24d13dff36f5d2510244c01b7abf4..74fb1223c2f5bbdcfecd214fa4871db8e93f01b3 100644 (file)
@@ -912,6 +912,12 @@ void nfs_file_clear_open_context(struct file *filp)
        if (ctx) {
                struct inode *inode = d_inode(ctx->dentry);
 
+               /*
+                * We fatal error on write before. Try to writeback
+                * every page again.
+                */
+               if (ctx->error < 0)
+                       invalidate_inode_pages2(inode->i_mapping);
                filp->private_data = NULL;
                spin_lock(&inode->i_lock);
                list_move_tail(&ctx->list, &NFS_I(inode)->open_files);
index 313d55402238fd2f0328ef94d57e13cfd0da3e7c..68f773dc226ece4e5cf1657628c6cf2e2646016b 100644 (file)
@@ -711,3 +711,17 @@ static inline u32 nfs_stateid_hash(nfs4_stateid *stateid)
        return 0;
 }
 #endif
+
+static inline bool nfs_error_is_fatal(int err)
+{
+       switch (err) {
+       case -ERESTARTSYS:
+       case -EIO:
+       case -ENOSPC:
+       case -EROFS:
+       case -E2BIG:
+               return true;
+       default:
+               return false;
+       }
+}
index 0fb3552ccfbef1328a175a00b5c07bc41c5df002..580207bc52a5deeffbb955add12d179a3be70614 100644 (file)
@@ -904,18 +904,9 @@ send_layoutget(struct pnfs_layout_hdr *lo,
                lseg = nfs4_proc_layoutget(lgp, gfp_flags);
        } while (lseg == ERR_PTR(-EAGAIN));
 
-       if (IS_ERR(lseg)) {
-               switch (PTR_ERR(lseg)) {
-               case -ERESTARTSYS:
-               case -EIO:
-               case -ENOSPC:
-               case -EROFS:
-               case -E2BIG:
-                       break;
-               default:
-                       return NULL;
-               }
-       } else
+       if (IS_ERR(lseg) && !nfs_error_is_fatal(PTR_ERR(lseg)))
+               lseg = NULL;
+       else
                pnfs_layout_clear_fail_bit(lo,
                                pnfs_iomode_to_fail_bit(range->iomode));
 
index 0bb580174cb3d4b529c46bffaa4bb938ca3308b8..eb31e23e7defa5a1cdb494aae35b18ccd3a30a0f 100644 (file)
@@ -85,6 +85,23 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
 }
 EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
 
+static void nfs_readpage_release(struct nfs_page *req)
+{
+       struct inode *inode = d_inode(req->wb_context->dentry);
+
+       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_page_group_sync_on_bit(req, PG_UNLOCKPAGE)) {
+               if (PageUptodate(req->wb_page))
+                       nfs_readpage_to_fscache(inode, req->wb_page, 0);
+
+               unlock_page(req->wb_page);
+       }
+       nfs_release_request(req);
+}
+
 int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
                       struct page *page)
 {
@@ -106,7 +123,10 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
 
        nfs_pageio_init_read(&pgio, inode, false,
                             &nfs_async_read_completion_ops);
-       nfs_pageio_add_request(&pgio, new);
+       if (!nfs_pageio_add_request(&pgio, new)) {
+               nfs_list_remove_request(new);
+               nfs_readpage_release(new);
+       }
        nfs_pageio_complete(&pgio);
 
        /* It doesn't make sense to do mirrored reads! */
@@ -118,23 +138,6 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
        return pgio.pg_error < 0 ? pgio.pg_error : 0;
 }
 
-static void nfs_readpage_release(struct nfs_page *req)
-{
-       struct inode *inode = d_inode(req->wb_context->dentry);
-
-       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_page_group_sync_on_bit(req, PG_UNLOCKPAGE)) {
-               if (PageUptodate(req->wb_page))
-                       nfs_readpage_to_fscache(inode, req->wb_page, 0);
-
-               unlock_page(req->wb_page);
-       }
-       nfs_release_request(req);
-}
-
 static void nfs_page_group_set_uptodate(struct nfs_page *req)
 {
        if (nfs_page_group_sync_on_bit(req, PG_UPTODATE))
@@ -361,6 +364,8 @@ readpage_async_filler(void *data, struct page *page)
        if (len < PAGE_CACHE_SIZE)
                zero_user_segment(page, len, PAGE_CACHE_SIZE);
        if (!nfs_pageio_add_request(desc->pgio, new)) {
+               nfs_list_remove_request(new);
+               nfs_readpage_release(new);
                error = desc->pgio->pg_error;
                goto out_unlock;
        }
index 7b93164069307346ea43f130e3883959ae6576b6..9dafb08ddae533981f149ecebfd83d5f01625cb2 100644 (file)
@@ -545,6 +545,15 @@ try_again:
        return head;
 }
 
+static void nfs_write_error_remove_page(struct nfs_page *req)
+{
+       nfs_unlock_request(req);
+       nfs_end_page_writeback(req);
+       nfs_release_request(req);
+       generic_error_remove_page(page_file_mapping(req->wb_page),
+                                 req->wb_page);
+}
+
 /*
  * Find an associated nfs write request, and prepare to flush it out
  * May return an error if the user signalled nfs_wait_on_request().
@@ -567,8 +576,19 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
 
        ret = 0;
        if (!nfs_pageio_add_request(pgio, req)) {
-               nfs_redirty_request(req);
                ret = pgio->pg_error;
+               /*
+                * Remove the problematic req upon fatal errors,
+                * while other dirty pages can still be around
+                * until they get flushed.
+                */
+               if (nfs_error_is_fatal(ret)) {
+                       nfs_context_set_write_error(req->wb_context, ret);
+                       nfs_write_error_remove_page(req);
+               } else {
+                       nfs_redirty_request(req);
+                       ret = -EAGAIN;
+               }
        } else
                nfs_add_stats(page_file_mapping(page)->host,
                                NFSIOS_WRITEPAGES, 1);