Merge branch 'x86-cleanups-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / fs / nfs / nfs42proc.c
index 5196bfa..1fe83e0 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (c) 2014 Anna Schumaker <Anna.Schumaker@Netapp.com>
  */
 #include <linux/fs.h>
+#include <linux/sunrpc/addr.h>
 #include <linux/sunrpc/sched.h>
 #include <linux/nfs.h>
 #include <linux/nfs3.h>
 #include "pnfs.h"
 #include "nfs4session.h"
 #include "internal.h"
+#include "delegation.h"
 
 #define NFSDBG_FACILITY NFSDBG_PROC
 static int nfs42_do_offload_cancel_async(struct file *dst, nfs4_stateid *std);
 
+static void nfs42_set_netaddr(struct file *filep, struct nfs42_netaddr *naddr)
+{
+       struct nfs_client *clp = (NFS_SERVER(file_inode(filep)))->nfs_client;
+       unsigned short port = 2049;
+
+       rcu_read_lock();
+       naddr->netid_len = scnprintf(naddr->netid,
+                                       sizeof(naddr->netid), "%s",
+                                       rpc_peeraddr2str(clp->cl_rpcclient,
+                                       RPC_DISPLAY_NETID));
+       naddr->addr_len = scnprintf(naddr->addr,
+                                       sizeof(naddr->addr),
+                                       "%s.%u.%u",
+                                       rpc_peeraddr2str(clp->cl_rpcclient,
+                                       RPC_DISPLAY_ADDR),
+                                       port >> 8, port & 255);
+       rcu_read_unlock();
+}
+
 static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
                struct nfs_lock_context *lock, loff_t offset, loff_t len)
 {
@@ -28,7 +49,7 @@ static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
                .falloc_fh      = NFS_FH(inode),
                .falloc_offset  = offset,
                .falloc_length  = len,
-               .falloc_bitmask = server->cache_consistency_bitmask,
+               .falloc_bitmask = nfs4_fattr_bitmap,
        };
        struct nfs42_falloc_res res = {
                .falloc_server  = server,
@@ -132,22 +153,26 @@ out_unlock:
 }
 
 static int handle_async_copy(struct nfs42_copy_res *res,
-                            struct nfs_server *server,
+                            struct nfs_server *dst_server,
+                            struct nfs_server *src_server,
                             struct file *src,
                             struct file *dst,
-                            nfs4_stateid *src_stateid)
+                            nfs4_stateid *src_stateid,
+                            bool *restart)
 {
        struct nfs4_copy_state *copy, *tmp_copy;
        int status = NFS4_OK;
        bool found_pending = false;
-       struct nfs_open_context *ctx = nfs_file_open_context(dst);
+       struct nfs_open_context *dst_ctx = nfs_file_open_context(dst);
+       struct nfs_open_context *src_ctx = nfs_file_open_context(src);
 
        copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
        if (!copy)
                return -ENOMEM;
 
-       spin_lock(&server->nfs_client->cl_lock);
-       list_for_each_entry(tmp_copy, &server->nfs_client->pending_cb_stateids,
+       spin_lock(&dst_server->nfs_client->cl_lock);
+       list_for_each_entry(tmp_copy,
+                               &dst_server->nfs_client->pending_cb_stateids,
                                copies) {
                if (memcmp(&res->write_res.stateid, &tmp_copy->stateid,
                                NFS4_STATEID_SIZE))
@@ -157,7 +182,7 @@ static int handle_async_copy(struct nfs42_copy_res *res,
                break;
        }
        if (found_pending) {
-               spin_unlock(&server->nfs_client->cl_lock);
+               spin_unlock(&dst_server->nfs_client->cl_lock);
                kfree(copy);
                copy = tmp_copy;
                goto out;
@@ -165,19 +190,32 @@ static int handle_async_copy(struct nfs42_copy_res *res,
 
        memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
        init_completion(&copy->completion);
-       copy->parent_state = ctx->state;
+       copy->parent_dst_state = dst_ctx->state;
+       copy->parent_src_state = src_ctx->state;
 
-       list_add_tail(&copy->copies, &server->ss_copies);
-       spin_unlock(&server->nfs_client->cl_lock);
+       list_add_tail(&copy->copies, &dst_server->ss_copies);
+       spin_unlock(&dst_server->nfs_client->cl_lock);
+
+       if (dst_server != src_server) {
+               spin_lock(&src_server->nfs_client->cl_lock);
+               list_add_tail(&copy->src_copies, &src_server->ss_copies);
+               spin_unlock(&src_server->nfs_client->cl_lock);
+       }
 
        status = wait_for_completion_interruptible(&copy->completion);
-       spin_lock(&server->nfs_client->cl_lock);
+       spin_lock(&dst_server->nfs_client->cl_lock);
        list_del_init(&copy->copies);
-       spin_unlock(&server->nfs_client->cl_lock);
+       spin_unlock(&dst_server->nfs_client->cl_lock);
+       if (dst_server != src_server) {
+               spin_lock(&src_server->nfs_client->cl_lock);
+               list_del_init(&copy->src_copies);
+               spin_unlock(&src_server->nfs_client->cl_lock);
+       }
        if (status == -ERESTARTSYS) {
                goto out_cancel;
-       } else if (copy->flags) {
+       } else if (copy->flags || copy->error == NFS4ERR_PARTNER_NO_AUTH) {
                status = -EAGAIN;
+               *restart = true;
                goto out_cancel;
        }
 out:
@@ -185,12 +223,14 @@ out:
        memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf));
        status = -copy->error;
 
+out_free:
        kfree(copy);
        return status;
 out_cancel:
        nfs42_do_offload_cancel_async(dst, &copy->stateid);
-       kfree(copy);
-       return status;
+       if (!nfs42_files_from_same_server(src, dst))
+               nfs42_do_offload_cancel_async(src, src_stateid);
+       goto out_free;
 }
 
 static int process_copy_commit(struct file *dst, loff_t pos_dst,
@@ -222,7 +262,10 @@ static ssize_t _nfs42_proc_copy(struct file *src,
                                struct file *dst,
                                struct nfs_lock_context *dst_lock,
                                struct nfs42_copy_args *args,
-                               struct nfs42_copy_res *res)
+                               struct nfs42_copy_res *res,
+                               struct nl4_server *nss,
+                               nfs4_stateid *cnr_stateid,
+                               bool *restart)
 {
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY],
@@ -230,17 +273,23 @@ static ssize_t _nfs42_proc_copy(struct file *src,
                .rpc_resp = res,
        };
        struct inode *dst_inode = file_inode(dst);
-       struct nfs_server *server = NFS_SERVER(dst_inode);
+       struct inode *src_inode = file_inode(src);
+       struct nfs_server *dst_server = NFS_SERVER(dst_inode);
+       struct nfs_server *src_server = NFS_SERVER(src_inode);
        loff_t pos_src = args->src_pos;
        loff_t pos_dst = args->dst_pos;
        size_t count = args->count;
        ssize_t status;
 
-       status = nfs4_set_rw_stateid(&args->src_stateid, src_lock->open_context,
-                                    src_lock, FMODE_READ);
-       if (status)
-               return status;
-
+       if (nss) {
+               args->cp_src = nss;
+               nfs4_stateid_copy(&args->src_stateid, cnr_stateid);
+       } else {
+               status = nfs4_set_rw_stateid(&args->src_stateid,
+                               src_lock->open_context, src_lock, FMODE_READ);
+               if (status)
+                       return status;
+       }
        status = nfs_filemap_write_and_wait_range(file_inode(src)->i_mapping,
                        pos_src, pos_src + (loff_t)count - 1);
        if (status)
@@ -262,13 +311,15 @@ static ssize_t _nfs42_proc_copy(struct file *src,
                if (!res->commit_res.verf)
                        return -ENOMEM;
        }
+       set_bit(NFS_CLNT_SRC_SSC_COPY_STATE,
+               &src_lock->open_context->state->flags);
        set_bit(NFS_CLNT_DST_SSC_COPY_STATE,
                &dst_lock->open_context->state->flags);
 
-       status = nfs4_call_sync(server->client, server, &msg,
+       status = nfs4_call_sync(dst_server->client, dst_server, &msg,
                                &args->seq_args, &res->seq_res, 0);
        if (status == -ENOTSUPP)
-               server->caps &= ~NFS_CAP_COPY;
+               dst_server->caps &= ~NFS_CAP_COPY;
        if (status)
                goto out;
 
@@ -280,8 +331,8 @@ static ssize_t _nfs42_proc_copy(struct file *src,
        }
 
        if (!res->synchronous) {
-               status = handle_async_copy(res, server, src, dst,
-                               &args->src_stateid);
+               status = handle_async_copy(res, dst_server, src_server, src,
+                               dst, &args->src_stateid, restart);
                if (status)
                        return status;
        }
@@ -304,8 +355,9 @@ out:
 }
 
 ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
-                       struct file *dst, loff_t pos_dst,
-                       size_t count)
+                       struct file *dst, loff_t pos_dst, size_t count,
+                       struct nl4_server *nss,
+                       nfs4_stateid *cnr_stateid, bool sync)
 {
        struct nfs_server *server = NFS_SERVER(file_inode(dst));
        struct nfs_lock_context *src_lock;
@@ -316,7 +368,7 @@ ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
                .dst_fh         = NFS_FH(file_inode(dst)),
                .dst_pos        = pos_dst,
                .count          = count,
-               .sync           = false,
+               .sync           = sync,
        };
        struct nfs42_copy_res res;
        struct nfs4_exception src_exception = {
@@ -328,6 +380,7 @@ ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
                .stateid        = &args.dst_stateid,
        };
        ssize_t err, err2;
+       bool restart = false;
 
        src_lock = nfs_get_lock_context(nfs_file_open_context(src));
        if (IS_ERR(src_lock))
@@ -347,21 +400,33 @@ ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
                inode_lock(file_inode(dst));
                err = _nfs42_proc_copy(src, src_lock,
                                dst, dst_lock,
-                               &args, &res);
+                               &args, &res,
+                               nss, cnr_stateid, &restart);
                inode_unlock(file_inode(dst));
 
                if (err >= 0)
                        break;
-               if (err == -ENOTSUPP) {
+               if (err == -ENOTSUPP &&
+                               nfs42_files_from_same_server(src, dst)) {
                        err = -EOPNOTSUPP;
                        break;
                } else if (err == -EAGAIN) {
-                       dst_exception.retry = 1;
-                       continue;
+                       if (!restart) {
+                               dst_exception.retry = 1;
+                               continue;
+                       }
+                       break;
                } else if (err == -NFS4ERR_OFFLOAD_NO_REQS && !args.sync) {
                        args.sync = true;
                        dst_exception.retry = 1;
                        continue;
+               } else if ((err == -ESTALE ||
+                               err == -NFS4ERR_OFFLOAD_DENIED ||
+                               err == -ENOTSUPP) &&
+                               !nfs42_files_from_same_server(src, dst)) {
+                       nfs42_do_offload_cancel_async(src, &args.src_stateid);
+                       err = -EOPNOTSUPP;
+                       break;
                }
 
                err2 = nfs4_handle_exception(server, err, &src_exception);
@@ -459,6 +524,76 @@ static int nfs42_do_offload_cancel_async(struct file *dst,
        return status;
 }
 
+static int _nfs42_proc_copy_notify(struct file *src, struct file *dst,
+                                  struct nfs42_copy_notify_args *args,
+                                  struct nfs42_copy_notify_res *res)
+{
+       struct nfs_server *src_server = NFS_SERVER(file_inode(src));
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY_NOTIFY],
+               .rpc_argp = args,
+               .rpc_resp = res,
+       };
+       int status;
+       struct nfs_open_context *ctx;
+       struct nfs_lock_context *l_ctx;
+
+       ctx = get_nfs_open_context(nfs_file_open_context(src));
+       l_ctx = nfs_get_lock_context(ctx);
+       if (IS_ERR(l_ctx))
+               return PTR_ERR(l_ctx);
+
+       status = nfs4_set_rw_stateid(&args->cna_src_stateid, ctx, l_ctx,
+                                    FMODE_READ);
+       nfs_put_lock_context(l_ctx);
+       if (status)
+               return status;
+
+       status = nfs4_call_sync(src_server->client, src_server, &msg,
+                               &args->cna_seq_args, &res->cnr_seq_res, 0);
+       if (status == -ENOTSUPP)
+               src_server->caps &= ~NFS_CAP_COPY_NOTIFY;
+
+       put_nfs_open_context(nfs_file_open_context(src));
+       return status;
+}
+
+int nfs42_proc_copy_notify(struct file *src, struct file *dst,
+                               struct nfs42_copy_notify_res *res)
+{
+       struct nfs_server *src_server = NFS_SERVER(file_inode(src));
+       struct nfs42_copy_notify_args *args;
+       struct nfs4_exception exception = {
+               .inode = file_inode(src),
+       };
+       int status;
+
+       if (!(src_server->caps & NFS_CAP_COPY_NOTIFY))
+               return -EOPNOTSUPP;
+
+       args = kzalloc(sizeof(struct nfs42_copy_notify_args), GFP_NOFS);
+       if (args == NULL)
+               return -ENOMEM;
+
+       args->cna_src_fh  = NFS_FH(file_inode(src)),
+       args->cna_dst.nl4_type = NL4_NETADDR;
+       nfs42_set_netaddr(dst, &args->cna_dst.u.nl4_addr);
+       exception.stateid = &args->cna_src_stateid;
+
+       do {
+               status = _nfs42_proc_copy_notify(src, dst, args, res);
+               if (status == -ENOTSUPP) {
+                       status = -EOPNOTSUPP;
+                       goto out;
+               }
+               status = nfs4_handle_exception(src_server, status, &exception);
+       } while (exception.retry);
+
+out:
+       kfree(args);
+       return status;
+}
+
 static loff_t _nfs42_proc_llseek(struct file *filep,
                struct nfs_lock_context *lock, loff_t offset, int whence)
 {