NFS: add COPY_NOTIFY operation

Try using the delegation stateid, then the open stateid.

Only NL4_NETATTR, No support for NL4_NAME and NL4_URL.
Allow only one source server address to be returned for now.

To distinguish between same server copy offload ("intra") and
a copy between different server ("inter"), do a check of server
owner identity and also make sure server is capable of doing
a copy offload.

Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
This commit is contained in:
Olga Kornievskaia
2019-06-04 16:14:30 -04:00
committed by Olga Kornievskaia
parent f9bdad8ca8
commit 0491567b51
11 changed files with 323 additions and 2 deletions

View 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>
@@ -15,10 +16,30 @@
#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)
{
@@ -459,6 +480,76 @@ static int nfs42_do_offload_cancel_async(struct file *dst,
return status;
}
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)
{