]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - fs/nfs/write.c
NFS: Add a few more fatal I/O errors to nfs_error_is_fatal()
[mirror_ubuntu-artful-kernel.git] / fs / nfs / write.c
index abb2c8a3be42e4755f747c62a1cec5466f13ee77..2e654940478f92eea2f6075cc0492aa35e2e1660 100644 (file)
@@ -60,14 +60,28 @@ static mempool_t *nfs_wdata_mempool;
 static struct kmem_cache *nfs_cdata_cachep;
 static mempool_t *nfs_commit_mempool;
 
-struct nfs_commit_data *nfs_commitdata_alloc(void)
+struct nfs_commit_data *nfs_commitdata_alloc(bool never_fail)
 {
-       struct nfs_commit_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOIO);
+       struct nfs_commit_data *p;
 
-       if (p) {
-               memset(p, 0, sizeof(*p));
-               INIT_LIST_HEAD(&p->pages);
+       if (never_fail)
+               p = mempool_alloc(nfs_commit_mempool, GFP_NOIO);
+       else {
+               /* It is OK to do some reclaim, not no safe to wait
+                * for anything to be returned to the pool.
+                * mempool_alloc() cannot handle that particular combination,
+                * so we need two separate attempts.
+                */
+               p = mempool_alloc(nfs_commit_mempool, GFP_NOWAIT);
+               if (!p)
+                       p = kmem_cache_alloc(nfs_cdata_cachep, GFP_NOIO |
+                                            __GFP_NOWARN | __GFP_NORETRY);
+               if (!p)
+                       return NULL;
        }
+
+       memset(p, 0, sizeof(*p));
+       INIT_LIST_HEAD(&p->pages);
        return p;
 }
 EXPORT_SYMBOL_GPL(nfs_commitdata_alloc);
@@ -82,8 +96,10 @@ static struct nfs_pgio_header *nfs_writehdr_alloc(void)
 {
        struct nfs_pgio_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO);
 
-       if (p)
+       if (p) {
                memset(p, 0, sizeof(*p));
+               p->rw_mode = FMODE_WRITE;
+       }
        return p;
 }
 
@@ -548,9 +564,21 @@ 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);
+       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);
 }
 
 /*
@@ -575,6 +603,10 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
        WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags));
 
        ret = 0;
+       /* If there is a fatal error that covers this write, just exit */
+       if (nfs_error_is_fatal_on_server(req->wb_context->error))
+               goto out_launder;
+
        if (!nfs_pageio_add_request(pgio, req)) {
                ret = pgio->pg_error;
                /*
@@ -584,10 +616,8 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
                 */
                if (nfs_error_is_fatal(ret)) {
                        nfs_context_set_write_error(req->wb_context, ret);
-                       if (launder) {
-                               nfs_write_error_remove_page(req);
-                               goto out;
-                       }
+                       if (launder)
+                               goto out_launder;
                }
                nfs_redirty_request(req);
                ret = -EAGAIN;
@@ -596,6 +626,9 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
                                NFSIOS_WRITEPAGES, 1);
 out:
        return ret;
+out_launder:
+       nfs_write_error_remove_page(req);
+       return ret;
 }
 
 static int nfs_do_writepage(struct page *page, struct writeback_control *wbc,
@@ -1368,7 +1401,7 @@ void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
                pg_ops = server->pnfs_curr_ld->pg_write_ops;
 #endif
        nfs_pageio_init(pgio, inode, pg_ops, compl_ops, &nfs_rw_write_ops,
-                       server->wsize, ioflags);
+                       server->wsize, ioflags, GFP_NOIO);
 }
 EXPORT_SYMBOL_GPL(nfs_pageio_init_write);
 
@@ -1705,19 +1738,13 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how,
        if (list_empty(head))
                return 0;
 
-       data = nfs_commitdata_alloc();
-
-       if (!data)
-               goto out_bad;
+       data = nfs_commitdata_alloc(true);
 
        /* Set up the argument struct */
        nfs_init_commit(data, head, NULL, cinfo);
        atomic_inc(&cinfo->mds->rpcs_out);
        return nfs_initiate_commit(NFS_CLIENT(inode), data, NFS_PROTO(inode),
                                   data->mds_ops, how, 0);
- out_bad:
-       nfs_retry_commit(head, NULL, cinfo, 0);
-       return -ENOMEM;
 }
 
 int nfs_commit_file(struct file *file, struct nfs_write_verifier *verf)
@@ -2108,7 +2135,6 @@ void nfs_destroy_writepagecache(void)
 }
 
 static const struct nfs_rw_ops nfs_rw_write_ops = {
-       .rw_mode                = FMODE_WRITE,
        .rw_alloc_header        = nfs_writehdr_alloc,
        .rw_free_header         = nfs_writehdr_free,
        .rw_done                = nfs_writeback_done,