NFS: Create a common rpc_call_ops struct
[firefly-linux-kernel-4.4.55.git] / fs / nfs / write.c
index 9a3b6a4cd6b9581a037f2508e5b6d4aca565cd93..d877f15fb31a1c50660eea7b8897136fa97597ff 100644 (file)
  * Local function declarations
  */
 static void nfs_redirty_request(struct nfs_page *req);
-static const struct rpc_call_ops nfs_write_common_ops;
 static const struct rpc_call_ops nfs_commit_ops;
 static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops;
 static const struct nfs_commit_completion_ops nfs_commit_completion_ops;
+static const struct nfs_rw_ops nfs_rw_write_ops;
 
 static struct kmem_cache *nfs_wdata_cachep;
 static mempool_t *nfs_wdata_mempool;
@@ -70,76 +70,19 @@ void nfs_commit_free(struct nfs_commit_data *p)
 }
 EXPORT_SYMBOL_GPL(nfs_commit_free);
 
-struct nfs_write_header *nfs_writehdr_alloc(void)
+static struct nfs_rw_header *nfs_writehdr_alloc(void)
 {
-       struct nfs_write_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO);
-
-       if (p) {
-               struct nfs_pgio_header *hdr = &p->header;
+       struct nfs_rw_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO);
 
+       if (p)
                memset(p, 0, sizeof(*p));
-               INIT_LIST_HEAD(&hdr->pages);
-               INIT_LIST_HEAD(&hdr->rpc_list);
-               spin_lock_init(&hdr->lock);
-               atomic_set(&hdr->refcnt, 0);
-               hdr->verf = &p->verf;
-       }
        return p;
 }
-EXPORT_SYMBOL_GPL(nfs_writehdr_alloc);
 
-static struct nfs_write_data *nfs_writedata_alloc(struct nfs_pgio_header *hdr,
-                                                 unsigned int pagecount)
+static void nfs_writehdr_free(struct nfs_rw_header *whdr)
 {
-       struct nfs_write_data *data, *prealloc;
-
-       prealloc = &container_of(hdr, struct nfs_write_header, header)->rpc_data;
-       if (prealloc->header == NULL)
-               data = prealloc;
-       else
-               data = kzalloc(sizeof(*data), GFP_KERNEL);
-       if (!data)
-               goto out;
-
-       if (nfs_pgarray_set(&data->pages, pagecount)) {
-               data->header = hdr;
-               atomic_inc(&hdr->refcnt);
-       } else {
-               if (data != prealloc)
-                       kfree(data);
-               data = NULL;
-       }
-out:
-       return data;
-}
-
-void nfs_writehdr_free(struct nfs_pgio_header *hdr)
-{
-       struct nfs_write_header *whdr = container_of(hdr, struct nfs_write_header, header);
        mempool_free(whdr, nfs_wdata_mempool);
 }
-EXPORT_SYMBOL_GPL(nfs_writehdr_free);
-
-void nfs_writedata_release(struct nfs_write_data *wdata)
-{
-       struct nfs_pgio_header *hdr = wdata->header;
-       struct nfs_write_header *write_header = container_of(hdr, struct nfs_write_header, header);
-
-       put_nfs_open_context(wdata->args.context);
-       if (wdata->pages.pagevec != wdata->pages.page_array)
-               kfree(wdata->pages.pagevec);
-       if (wdata == &write_header->rpc_data) {
-               wdata->header = NULL;
-               wdata = NULL;
-       }
-       if (atomic_dec_and_test(&hdr->refcnt))
-               hdr->completion_ops->completion(hdr);
-       /* Note: we only free the rpc_task after callbacks are done.
-        * See the comment in rpc_free_task() for why
-        */
-       kfree(wdata);
-}
-EXPORT_SYMBOL_GPL(nfs_writedata_release);
 
 static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error)
 {
@@ -354,10 +297,8 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
        struct nfs_pageio_descriptor pgio;
        int err;
 
-       NFS_PROTO(page_file_mapping(page)->host)->write_pageio_init(&pgio,
-                                                         page->mapping->host,
-                                                         wb_priority(wbc),
-                                                         &nfs_async_write_completion_ops);
+       nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc),
+                               false, &nfs_async_write_completion_ops);
        err = nfs_do_writepage(page, wbc, &pgio);
        nfs_pageio_complete(&pgio);
        if (err < 0)
@@ -400,7 +341,8 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
 
        nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);
 
-       NFS_PROTO(inode)->write_pageio_init(&pgio, inode, wb_priority(wbc), &nfs_async_write_completion_ops);
+       nfs_pageio_init_write(&pgio, inode, wb_priority(wbc), false,
+                               &nfs_async_write_completion_ops);
        err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
        nfs_pageio_complete(&pgio);
 
@@ -583,7 +525,7 @@ nfs_clear_request_commit(struct nfs_page *req)
 }
 
 static inline
-int nfs_write_need_commit(struct nfs_write_data *data)
+int nfs_write_need_commit(struct nfs_pgio_data *data)
 {
        if (data->verf.committed == NFS_DATA_SYNC)
                return data->header->lseg == NULL;
@@ -614,7 +556,7 @@ nfs_clear_request_commit(struct nfs_page *req)
 }
 
 static inline
-int nfs_write_need_commit(struct nfs_write_data *data)
+int nfs_write_need_commit(struct nfs_pgio_data *data)
 {
        return 0;
 }
@@ -645,7 +587,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
                        goto next;
                }
                if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) {
-                       memcpy(&req->wb_verf, &hdr->verf->verifier, sizeof(req->wb_verf));
+                       memcpy(&req->wb_verf, &hdr->verf.verifier, sizeof(req->wb_verf));
                        nfs_mark_request_commit(req, hdr->lseg, &cinfo);
                        goto next;
                }
@@ -991,7 +933,7 @@ static int flush_task_priority(int how)
 }
 
 int nfs_initiate_write(struct rpc_clnt *clnt,
-                      struct nfs_write_data *data,
+                      struct nfs_pgio_data *data,
                       const struct rpc_call_ops *call_ops,
                       int how, int flags)
 {
@@ -1048,7 +990,7 @@ EXPORT_SYMBOL_GPL(nfs_initiate_write);
 /*
  * Set up the argument/result storage required for the RPC call.
  */
-static void nfs_write_rpcsetup(struct nfs_write_data *data,
+static void nfs_write_rpcsetup(struct nfs_pgio_data *data,
                unsigned int count, unsigned int offset,
                int how, struct nfs_commit_info *cinfo)
 {
@@ -1083,7 +1025,7 @@ static void nfs_write_rpcsetup(struct nfs_write_data *data,
        nfs_fattr_init(&data->fattr);
 }
 
-static int nfs_do_write(struct nfs_write_data *data,
+static int nfs_do_write(struct nfs_pgio_data *data,
                const struct rpc_call_ops *call_ops,
                int how)
 {
@@ -1096,13 +1038,13 @@ static int nfs_do_multiple_writes(struct list_head *head,
                const struct rpc_call_ops *call_ops,
                int how)
 {
-       struct nfs_write_data *data;
+       struct nfs_pgio_data *data;
        int ret = 0;
 
        while (!list_empty(head)) {
                int ret2;
 
-               data = list_first_entry(head, struct nfs_write_data, list);
+               data = list_first_entry(head, struct nfs_pgio_data, list);
                list_del_init(&data->list);
                
                ret2 = nfs_do_write(data, call_ops, how);
@@ -1145,10 +1087,10 @@ static void nfs_flush_error(struct nfs_pageio_descriptor *desc,
 {
        set_bit(NFS_IOHDR_REDO, &hdr->flags);
        while (!list_empty(&hdr->rpc_list)) {
-               struct nfs_write_data *data = list_first_entry(&hdr->rpc_list,
-                               struct nfs_write_data, list);
+               struct nfs_pgio_data *data = list_first_entry(&hdr->rpc_list,
+                               struct nfs_pgio_data, list);
                list_del(&data->list);
-               nfs_writedata_release(data);
+               nfs_pgio_data_release(data);
        }
        desc->pg_completion_ops->error_cleanup(&desc->pg_list);
 }
@@ -1162,7 +1104,7 @@ static int nfs_flush_multi(struct nfs_pageio_descriptor *desc,
 {
        struct nfs_page *req = hdr->req;
        struct page *page = req->wb_page;
-       struct nfs_write_data *data;
+       struct nfs_pgio_data *data;
        size_t wsize = desc->pg_bsize, nbytes;
        unsigned int offset;
        int requests = 0;
@@ -1181,7 +1123,7 @@ static int nfs_flush_multi(struct nfs_pageio_descriptor *desc,
        do {
                size_t len = min(nbytes, wsize);
 
-               data = nfs_writedata_alloc(hdr, 1);
+               data = nfs_pgio_data_alloc(hdr, 1);
                if (!data) {
                        nfs_flush_error(desc, hdr);
                        return -ENOMEM;
@@ -1195,7 +1137,7 @@ static int nfs_flush_multi(struct nfs_pageio_descriptor *desc,
        } while (nbytes != 0);
        nfs_list_remove_request(req);
        nfs_list_add_request(req, &hdr->pages);
-       desc->pg_rpc_callops = &nfs_write_common_ops;
+       desc->pg_rpc_callops = &nfs_pgio_common_ops;
        return 0;
 }
 
@@ -1212,11 +1154,11 @@ static int nfs_flush_one(struct nfs_pageio_descriptor *desc,
 {
        struct nfs_page         *req;
        struct page             **pages;
-       struct nfs_write_data   *data;
+       struct nfs_pgio_data    *data;
        struct list_head *head = &desc->pg_list;
        struct nfs_commit_info cinfo;
 
-       data = nfs_writedata_alloc(hdr, nfs_page_array_len(desc->pg_base,
+       data = nfs_pgio_data_alloc(hdr, nfs_page_array_len(desc->pg_base,
                                                           desc->pg_count));
        if (!data) {
                nfs_flush_error(desc, hdr);
@@ -1239,7 +1181,7 @@ static int nfs_flush_one(struct nfs_pageio_descriptor *desc,
        /* Set up the argument struct */
        nfs_write_rpcsetup(data, desc->pg_count, 0, desc->pg_ioflags, &cinfo);
        list_add(&data->list, &hdr->rpc_list);
-       desc->pg_rpc_callops = &nfs_write_common_ops;
+       desc->pg_rpc_callops = &nfs_pgio_common_ops;
        return 0;
 }
 
@@ -1254,17 +1196,17 @@ EXPORT_SYMBOL_GPL(nfs_generic_flush);
 
 static int nfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc)
 {
-       struct nfs_write_header *whdr;
+       struct nfs_rw_header *whdr;
        struct nfs_pgio_header *hdr;
        int ret;
 
-       whdr = nfs_writehdr_alloc();
+       whdr = nfs_rw_header_alloc(desc->pg_rw_ops);
        if (!whdr) {
                desc->pg_completion_ops->error_cleanup(&desc->pg_list);
                return -ENOMEM;
        }
        hdr = &whdr->header;
-       nfs_pgheader_init(desc, hdr, nfs_writehdr_free);
+       nfs_pgheader_init(desc, hdr, nfs_rw_header_free);
        atomic_inc(&hdr->refcnt);
        ret = nfs_generic_flush(desc, hdr);
        if (ret == 0)
@@ -1282,11 +1224,18 @@ static const struct nfs_pageio_ops nfs_pageio_write_ops = {
 };
 
 void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
-                              struct inode *inode, int ioflags,
+                              struct inode *inode, int ioflags, bool force_mds,
                               const struct nfs_pgio_completion_ops *compl_ops)
 {
-       nfs_pageio_init(pgio, inode, &nfs_pageio_write_ops, compl_ops,
-                               NFS_SERVER(inode)->wsize, ioflags);
+       struct nfs_server *server = NFS_SERVER(inode);
+       const struct nfs_pageio_ops *pg_ops = &nfs_pageio_write_ops;
+
+#ifdef CONFIG_NFS_V4_1
+       if (server->pnfs_curr_ld && !force_mds)
+               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);
 }
 EXPORT_SYMBOL_GPL(nfs_pageio_init_write);
 
@@ -1298,15 +1247,6 @@ void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio)
 EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds);
 
 
-void nfs_write_prepare(struct rpc_task *task, void *calldata)
-{
-       struct nfs_write_data *data = calldata;
-       int err;
-       err = NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data);
-       if (err)
-               rpc_exit(task, err);
-}
-
 void nfs_commit_prepare(struct rpc_task *task, void *calldata)
 {
        struct nfs_commit_data *data = calldata;
@@ -1314,23 +1254,8 @@ void nfs_commit_prepare(struct rpc_task *task, void *calldata)
        NFS_PROTO(data->inode)->commit_rpc_prepare(task, data);
 }
 
-/*
- * Handle a write reply that flushes a whole page.
- *
- * FIXME: There is an inherent race with invalidate_inode_pages and
- *       writebacks since the page->count is kept > 1 for as long
- *       as the page has a write request pending.
- */
-static void nfs_writeback_done_common(struct rpc_task *task, void *calldata)
-{
-       struct nfs_write_data   *data = calldata;
-
-       nfs_writeback_done(task, data);
-}
-
-static void nfs_writeback_release_common(void *calldata)
+static void nfs_writeback_release_common(struct nfs_pgio_data *data)
 {
-       struct nfs_write_data   *data = calldata;
        struct nfs_pgio_header *hdr = data->header;
        int status = data->task.tk_status;
 
@@ -1339,34 +1264,46 @@ static void nfs_writeback_release_common(void *calldata)
                if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags))
                        ; /* Do nothing */
                else if (!test_and_set_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags))
-                       memcpy(hdr->verf, &data->verf, sizeof(*hdr->verf));
-               else if (memcmp(hdr->verf, &data->verf, sizeof(*hdr->verf)))
+                       memcpy(&hdr->verf, &data->verf, sizeof(hdr->verf));
+               else if (memcmp(&hdr->verf, &data->verf, sizeof(hdr->verf)))
                        set_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags);
                spin_unlock(&hdr->lock);
        }
-       nfs_writedata_release(data);
 }
 
-static const struct rpc_call_ops nfs_write_common_ops = {
-       .rpc_call_prepare = nfs_write_prepare,
-       .rpc_call_done = nfs_writeback_done_common,
-       .rpc_release = nfs_writeback_release_common,
-};
+/*
+ * Special version of should_remove_suid() that ignores capabilities.
+ */
+static int nfs_should_remove_suid(const struct inode *inode)
+{
+       umode_t mode = inode->i_mode;
+       int kill = 0;
 
+       /* suid always must be killed */
+       if (unlikely(mode & S_ISUID))
+               kill = ATTR_KILL_SUID;
+
+       /*
+        * sgid without any exec bits is just a mandatory locking mark; leave
+        * it alone.  If some exec bits are set, it's a real sgid; kill it.
+        */
+       if (unlikely((mode & S_ISGID) && (mode & S_IXGRP)))
+               kill |= ATTR_KILL_SGID;
+
+       if (unlikely(kill && S_ISREG(mode)))
+               return kill;
+
+       return 0;
+}
 
 /*
  * This function is called when the WRITE call is complete.
  */
-void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
+static int nfs_writeback_done(struct rpc_task *task, struct nfs_pgio_data *data,
+                             struct inode *inode)
 {
-       struct nfs_writeargs    *argp = &data->args;
-       struct nfs_writeres     *resp = &data->res;
-       struct inode            *inode = data->header->inode;
        int status;
 
-       dprintk("NFS: %5u nfs_writeback_done (status %d)\n",
-               task->tk_pid, task->tk_status);
-
        /*
         * ->write_done will attempt to use post-op attributes to detect
         * conflicting writes by other clients.  A strict interpretation
@@ -1376,11 +1313,11 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
         */
        status = NFS_PROTO(inode)->write_done(task, data);
        if (status != 0)
-               return;
-       nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, resp->count);
+               return status;
+       nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, data->res.count);
 
 #if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
-       if (resp->verf->committed < argp->stable && task->tk_status >= 0) {
+       if (data->res.verf->committed < data->args.stable && task->tk_status >= 0) {
                /* We tried a write call, but the server did not
                 * commit data to stable storage even though we
                 * requested it.
@@ -1396,18 +1333,31 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
                        dprintk("NFS:       faulty NFS server %s:"
                                " (committed = %d) != (stable = %d)\n",
                                NFS_SERVER(inode)->nfs_client->cl_hostname,
-                               resp->verf->committed, argp->stable);
+                               data->res.verf->committed, data->args.stable);
                        complain = jiffies + 300 * HZ;
                }
        }
 #endif
-       if (task->tk_status < 0)
-               nfs_set_pgio_error(data->header, task->tk_status, argp->offset);
-       else if (resp->count < argp->count) {
+
+       /* Deal with the suid/sgid bit corner case */
+       if (nfs_should_remove_suid(inode))
+               nfs_mark_for_revalidate(inode);
+       return 0;
+}
+
+/*
+ * This function is called when the WRITE call is complete.
+ */
+static void nfs_writeback_result(struct rpc_task *task, struct nfs_pgio_data *data)
+{
+       struct nfs_pgio_args    *argp = &data->args;
+       struct nfs_pgio_res     *resp = &data->res;
+
+       if (resp->count < argp->count) {
                static unsigned long    complain;
 
                /* This a short write! */
-               nfs_inc_stats(inode, NFSIOS_SHORTWRITE);
+               nfs_inc_stats(data->header->inode, NFSIOS_SHORTWRITE);
 
                /* Has the server at least made some progress? */
                if (resp->count == 0) {
@@ -1874,7 +1824,7 @@ int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
 int __init nfs_init_writepagecache(void)
 {
        nfs_wdata_cachep = kmem_cache_create("nfs_write_data",
-                                            sizeof(struct nfs_write_header),
+                                            sizeof(struct nfs_rw_header),
                                             0, SLAB_HWCACHE_ALIGN,
                                             NULL);
        if (nfs_wdata_cachep == NULL)
@@ -1936,3 +1886,11 @@ void nfs_destroy_writepagecache(void)
        kmem_cache_destroy(nfs_wdata_cachep);
 }
 
+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_release             = nfs_writeback_release_common,
+       .rw_done                = nfs_writeback_done,
+       .rw_result              = nfs_writeback_result,
+};