2015-03-17 22:41:15

by Andy Adamson

[permalink] [raw]
Subject: [PATCH RFC 00/10] NFSv4.2 Inter server to server copy RFC

From: Andy Adamson <[email protected]>

Disclaimer: this code is in early development. Please excuse printk's etc.

This RFC is for us to determine if there are objections, suggestions, or comments on our approach. This code builds on top of Anna's intra-server copy patch set which uses the new vfs_copy_file_range call. This code can perform an inter-ssc copy using NFSv4.1 as the copy protocol; the destination server loads the nfs42-interserver-copy module, mounts the source server, sets up an NFS filep to pass to vfs_copy_file_range which READs the file to be copied over NFSv4.1. The destination server then cleans up. Multiple copies do not currently work.

Objectives:
-----------

- Use NFSv4.1 for the copy protocol betweent the source and destination servers to READ the data to be copied.
- Call the new vfs_copy_file_range function.
- Do not call NFSv4.1 OPEN from the destination server. Instead, use the COPY ca_src_stateid and the COPY SAVE_FH filehandle, which are the file handle and stateid from the OPEN on the client by the user.

Some questions
--------------
1) NFS interserver copy module

The basic design is for the destination server to load a new nfs42-interserver-copy NFS kernel module to setup the struct filp for vfs_copy_file_range to use.
- We prefer the 'load NFS module' design to other designs such as an upcall to a userland daemon. Comments?

2) NFS module connecting to the source server.

The module calls nfs_fs_mount using the NL4_NETADDR address in string form concatenated with ":/".
We need to end up with a struct file that represents an NFSv4.1 open file to use for the READ. The alloc_file function requires a vfsmount struct in the struct path argument.
Patch "NFS return the root_mnt via the raw data in nfs_fs_mount" makes the vfsmount struct from nfs_fs_mount available.

Comments on this approach?

Thanks

Andy Adamson


Andy Adamson (10):
NFS return the root_mnt via the raw data in nfs_fs_mount
NFS interserver ssc module
NFS add COPY_NOTIFY operation
NFSD add ca_source_server<> to COPY
NFSD add COPY_NOTIFY operation
NFS add ca_source_server<> to COPY
NFSD generalize nfsd4_compound_state flag names
NFSD: allow inter server COPY to have a STALE source server fh
NFSD: add nfs4interssc.c
NFSD nfs4 inter ssc copy

fs/nfs/Kconfig | 10 +
fs/nfs/Makefile | 3 +
fs/nfs/internal.h | 16 ++
fs/nfs/nfs42.h | 5 +-
fs/nfs/nfs42proc.c | 77 +++++++-
fs/nfs/nfs42xdr.c | 196 ++++++++++++++++++-
fs/nfs/nfs4_fs.h | 1 +
fs/nfs/nfs4client.c | 30 +++
fs/nfs/nfs4file.c | 32 ++-
fs/nfs/nfs4intercopy.c | 419 ++++++++++++++++++++++++++++++++++++++++
fs/nfs/nfs4proc.c | 8 +-
fs/nfs/nfs4state.c | 3 +
fs/nfs/nfs4super.c | 2 +
fs/nfs/nfs4xdr.c | 1 +
fs/nfs/super.c | 27 +++
fs/nfsd/Makefile | 2 +-
fs/nfsd/nfs4interssc.c | 110 +++++++++++
fs/nfsd/nfs4proc.c | 307 ++++++++++++++++++++++++++++-
fs/nfsd/nfs4state.c | 6 +-
fs/nfsd/nfs4xdr.c | 180 ++++++++++++++++-
fs/nfsd/nfsd.h | 2 +
fs/nfsd/xdr4.h | 40 +++-
include/linux/nfs4.h | 7 +
include/linux/nfs4intercopy.h | 44 +++++
include/linux/nfs_fs_sb.h | 1 +
include/linux/nfs_xdr.h | 54 ++++++
include/uapi/linux/nfs4_mount.h | 1 +
27 files changed, 1548 insertions(+), 36 deletions(-)
create mode 100644 fs/nfs/nfs4intercopy.c
create mode 100644 fs/nfsd/nfs4interssc.c
create mode 100644 include/linux/nfs4intercopy.h

--
1.8.3.1



2015-03-17 22:41:08

by Andy Adamson

[permalink] [raw]
Subject: [PATCH RFC 07/10] NFSD generalize nfsd4_compound_state flag names

From: Andy Adamson <[email protected]>

Allow for sid_flag field non-stateid use.

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfsd/nfs4proc.c | 8 ++++----
fs/nfsd/nfs4state.c | 6 +++---
fs/nfsd/xdr4.h | 6 +++---
3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 82240b6..7d899d2 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -537,9 +537,9 @@ nfsd4_restorefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return nfserr_restorefh;

fh_dup2(&cstate->current_fh, &cstate->save_fh);
- if (HAS_STATE_ID(cstate, SAVED_STATE_ID_FLAG)) {
+ if (HAS_CSTATE_FLAG(cstate, SAVED_STATE_ID_FLAG)) {
memcpy(&cstate->current_stateid, &cstate->save_stateid, sizeof(stateid_t));
- SET_STATE_ID(cstate, CURRENT_STATE_ID_FLAG);
+ SET_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG);
}
return nfs_ok;
}
@@ -552,9 +552,9 @@ nfsd4_savefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return nfserr_nofilehandle;

fh_dup2(&cstate->save_fh, &cstate->current_fh);
- if (HAS_STATE_ID(cstate, CURRENT_STATE_ID_FLAG)) {
+ if (HAS_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG)) {
memcpy(&cstate->save_stateid, &cstate->current_stateid, sizeof(stateid_t));
- SET_STATE_ID(cstate, SAVED_STATE_ID_FLAG);
+ SET_CSTATE_FLAG(cstate, SAVED_STATE_ID_FLAG);
}
return nfs_ok;
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index fc166a29..0f04290 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -6505,7 +6505,7 @@ nfs4_state_shutdown(void)
static void
get_stateid(struct nfsd4_compound_state *cstate, stateid_t *stateid)
{
- if (HAS_STATE_ID(cstate, CURRENT_STATE_ID_FLAG) && CURRENT_STATEID(stateid))
+ if (HAS_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG) && CURRENT_STATEID(stateid))
memcpy(stateid, &cstate->current_stateid, sizeof(stateid_t));
}

@@ -6514,14 +6514,14 @@ put_stateid(struct nfsd4_compound_state *cstate, stateid_t *stateid)
{
if (cstate->minorversion) {
memcpy(&cstate->current_stateid, stateid, sizeof(stateid_t));
- SET_STATE_ID(cstate, CURRENT_STATE_ID_FLAG);
+ SET_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG);
}
}

void
clear_current_stateid(struct nfsd4_compound_state *cstate)
{
- CLEAR_STATE_ID(cstate, CURRENT_STATE_ID_FLAG);
+ CLEAR_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG);
}

/*
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 97d80a6..4d6f2fe7 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -47,9 +47,9 @@
#define CURRENT_STATE_ID_FLAG (1<<0)
#define SAVED_STATE_ID_FLAG (1<<1)

-#define SET_STATE_ID(c, f) ((c)->sid_flags |= (f))
-#define HAS_STATE_ID(c, f) ((c)->sid_flags & (f))
-#define CLEAR_STATE_ID(c, f) ((c)->sid_flags &= ~(f))
+#define SET_CSTATE_FLAG(c, f) ((c)->sid_flags |= (f))
+#define HAS_CSTATE_FLAG(c, f) ((c)->sid_flags & (f))
+#define CLEAR_CSTATE_FLAG(c, f) ((c)->sid_flags &= ~(f))

struct nfsd4_compound_state {
struct svc_fh current_fh;
--
1.8.3.1


2015-03-17 22:41:11

by Andy Adamson

[permalink] [raw]
Subject: [PATCH RFC 02/10] NFS interserver ssc module

From: Andy Adamson <[email protected]>

This is the nfs42_interserver_copy module loaded by the destination server
to READ data from the source server

ssc_connect - calls nfs_try_mount using "src-server-ipaddr string:/"
where NL4_NETADDR uaddr is stringified for src-server-ipaddr.
- return vfsmount from nfs_fs_mount

scc_open - add d_instantiate and file alloc
add stateid and context
export a bunch of symbols
set vfsmount
ssc_open printks

ssc_disconnect - clean up open,read,and mount point

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfs/Kconfig | 10 +
fs/nfs/Makefile | 3 +
fs/nfs/internal.h | 14 ++
fs/nfs/nfs42proc.c | 2 +-
fs/nfs/nfs4_fs.h | 1 +
fs/nfs/nfs4client.c | 30 +++
fs/nfs/nfs4intercopy.c | 419 ++++++++++++++++++++++++++++++++++++++++++
fs/nfs/nfs4proc.c | 7 +-
fs/nfs/nfs4state.c | 3 +
include/linux/nfs4intercopy.h | 44 +++++
include/linux/nfs_xdr.h | 3 +
11 files changed, 532 insertions(+), 4 deletions(-)
create mode 100644 fs/nfs/nfs4intercopy.c
create mode 100644 include/linux/nfs4intercopy.h

diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
index c7abc10..4f750dd 100644
--- a/fs/nfs/Kconfig
+++ b/fs/nfs/Kconfig
@@ -113,6 +113,16 @@ config NFS_V4_2

If unsure, say N.

+config NFS_V4_2_INTER_SSC_COPY
+ tristate "NFS client support for NFSv4.2 Inter server side copy"
+ default m
+ depends on NFS_V4_2
+ help
+ This option enables support for inter server copy for minor version
+ 2 of the NFSv4 protocol in the kernel's NFS client.
+
+ If unsure, say N.
+
config PNFS_FILE_LAYOUT
tristate
depends on NFS_V4_1
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index 1e987ac..30eff12 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -34,3 +34,6 @@ obj-$(CONFIG_PNFS_FILE_LAYOUT) += filelayout/
obj-$(CONFIG_PNFS_OBJLAYOUT) += objlayout/
obj-$(CONFIG_PNFS_BLOCK) += blocklayout/
obj-$(CONFIG_PNFS_FLEXFILE_LAYOUT) += flexfilelayout/
+
+obj-$(CONFIG_NFS_V4_2_INTER_SSC_COPY) += nfs42-interserver-copy.o
+nfs42-interserver-copy-y := nfs4intercopy.o
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 473e241..c18c4fb 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -192,6 +192,9 @@ extern struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
unsigned int ds_retrans,
u32 minor_version,
rpc_authflavor_t au_flavor);
+extern struct nfs_client *nfs4_set_s2s_client(struct net *net, char *ipaddr,
+ const struct sockaddr *ss_addr,
+ int ss_addrlen);
extern struct rpc_clnt *nfs4_find_or_create_ds_client(struct nfs_client *,
struct inode *);
extern struct nfs_client *nfs3_set_ds_client(struct nfs_client *mds_clp,
@@ -515,6 +518,17 @@ extern int nfs40_walk_client_list(struct nfs_client *clp,
extern int nfs41_walk_client_list(struct nfs_client *clp,
struct nfs_client **result,
struct rpc_cred *cred);
+extern void __update_open_stateid(struct nfs4_state *state,
+ nfs4_stateid *open_stateid,
+ const nfs4_stateid *deleg_stateid,
+ fmode_t fmode);
+/* nfs4state.c for INTER SSC */
+extern struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server,
+ struct rpc_cred *cred,
+ gfp_t gfp_flags);
+extern struct nfs4_state *nfs4_get_open_state(struct inode *inode,
+ struct nfs4_state_owner *owner);
+extern void nfs4_put_state_owner(struct nfs4_state_owner *sp);

static inline struct inode *nfs_igrab_and_active(struct inode *inode)
{
diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index f3c0ac7..1ce9274 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -2,7 +2,7 @@
* Copyright (c) 2014 Anna Schumaker <[email protected]>
*/
#include <linux/fs.h>
-#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
#include <linux/nfs.h>
#include <linux/nfs3.h>
#include <linux/nfs4.h>
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index fdef424..ee86c40 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -229,6 +229,7 @@ int nfs4_replace_transport(struct nfs_server *server,
const struct nfs4_fs_locations *locations);

/* nfs4proc.c */
+extern int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *);
extern int nfs4_handle_exception(struct nfs_server *, int, struct nfs4_exception *);
extern int nfs4_call_sync(struct rpc_clnt *, struct nfs_server *,
struct rpc_message *, struct nfs4_sequence_args *,
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 86d6214..e332130 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -882,6 +882,36 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
EXPORT_SYMBOL_GPL(nfs4_set_ds_client);

/*
+ * Set up an NFSv42 inter server to server copy client.
+ */
+struct nfs_client *nfs4_set_s2s_client(struct net *net, char *ipaddr,
+ const struct sockaddr *ss_addr, int ss_addrlen)
+{
+ struct nfs_client_initdata cl_init = {
+ .addr = ss_addr,
+ .addrlen = ss_addrlen,
+ .nfs_mod = &nfs_v4,
+ .proto = IPPROTO_TCP,
+ .minorversion = 2,
+ .net = net,
+ };
+ struct rpc_timeout ss_timeout;
+ struct nfs_client *clp;
+
+ /* 600 = 60 sec timeout, 5 retrys */
+ nfs_init_timeout_values(&ss_timeout, IPPROTO_TCP, 600, 5);
+
+ /* for now use AUTH_UNIX. Will use RPCSEC_GSSv3 in time */
+ clp = nfs_get_client(&cl_init, &ss_timeout, ipaddr,
+ RPC_AUTH_UNIX);
+
+ dprintk("<-- %s %p\n", __func__, clp);
+ return clp;
+}
+EXPORT_SYMBOL_GPL(nfs4_set_s2s_client);
+
+
+/*
* Session has been established, and the client marked ready.
* Set the mount rsize and wsize with negotiated fore channel
* attributes which will be bound checked in nfs_server_set_fsinfo.
diff --git a/fs/nfs/nfs4intercopy.c b/fs/nfs/nfs4intercopy.c
new file mode 100644
index 0000000..0ed089f7
--- /dev/null
+++ b/fs/nfs/nfs4intercopy.c
@@ -0,0 +1,419 @@
+/*
+ * linux/fs/nfs/nfs4intercopy.c
+ *
+ * Copyright (C) 2014 Andy Adamson <[email protected]>
+ *
+ * nfs inter-server server-side copy READ implementation
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/nfs_fs.h>
+#include <linux/dcache.h>
+#include <linux/file.h>
+#include <linux/sunrpc/addr.h>
+#include "internal.h"
+#include "nfs4session.h"
+#include "netns.h"
+
+
+#define NFSDBG_FACILITY NFSDBG_CLIENT
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andy Adamson <[email protected]>");
+MODULE_DESCRIPTION("The NFSv4 Inter Server SSC driver");
+
+static DEFINE_SPINLOCK(nfs4_s2s_cache_lock);
+static LIST_HEAD(nfs4_s2s_client_cache);
+
+static int read_name_gen;
+#define SSC_READ_NAME_BODY "ssc_read_%d"
+#define SSC_READ_NAME_LEN 16
+
+static struct nfs42_ssc_client *
+_ssc_lookup_locked(char *ipaddr)
+{
+ struct nfs42_ssc_client *ssc;
+
+ list_for_each_entry(ssc, &nfs4_s2s_client_cache, sc_node)
+ if ((strlen(ipaddr) == strlen(ssc->sc_ipaddr)) &&
+ (memcmp(ipaddr, ssc->sc_ipaddr, strlen(ipaddr)) == 0))
+ return ssc;
+ return NULL;
+}
+
+/**
+ * Construct mount options:
+ * 'minorversion=1,vers=4,addr=<>,clientaddr=<>'
+ * size is 36 plus the two ip addr lengths.
+ */
+static int
+nfs4_make_ssc_rawdata(char *ipaddr, char *client_ipaddr, char **raw_data)
+{
+ char *rdp;
+
+ dprintk("%s ipaddr %s client_ipaddr %s\n", __func__, ipaddr,
+ client_ipaddr);
+ if (raw_data == NULL)
+ return -EINVAL;
+
+ rdp = kzalloc(64, GFP_KERNEL);
+ if (!rdp)
+ return -ENOMEM;
+
+ snprintf(rdp, 64, "minorversion=1,vers=4,addr=%s,clientaddr=%pI4",
+ ipaddr, client_ipaddr);
+
+ *raw_data = rdp;
+ return 0;
+}
+
+/**
+ * dummy fs type:
+ * With the raw_data having vers=4, nfs_fs_mount will
+ * grab the nfs4_fs_type from nfs4super.c.
+ * Need to look at the logic of validate mount data so that
+ * we can pass in a NULL fs_type and have the vers=x determing
+ * which file system to mount
+ */
+static struct file_system_type nfs4_dummy_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "nfs4-dummy",
+};
+
+static struct dentry *
+nfs4_ssc_mount(int flags, char *dev_name, char *raw_data)
+{
+ struct dentry *mnt_dentry = NULL;
+
+ mnt_dentry = nfs_fs_mount(&nfs4_dummy_fs_type, flags, dev_name,
+ (void *)raw_data);
+
+ /* allocated in nfs4_make_ssc_rawdata. XXXX change */
+ kfree(raw_data);
+ return mnt_dentry;
+}
+
+/*
+ * Given the source server address from a COPY call, NFSD sets up
+ * an NFSv42 connection to the source server.
+ *
+ * called with SVC_NET(rqstp)
+ */
+
+static struct nfs42_ssc_client *
+nfs4_ssc_connect(struct nfs42_netaddr *src, struct net *net, char *clientip)
+{
+ struct nfs42_ssc_client *ssc, *tmp_ssc;
+ struct dentry *mnt_dentry = NULL;
+ char *portstr;
+ int tmp[2], len, match_netid_len;
+ char *match_netid;
+ char *startsep = "", *endsep = "";
+ unsigned short port;
+ char *raw_data;
+ int status;
+
+ dprintk("--> %s clientip %s\n", __func__, clientip);
+ /* Freed in ssc_disconnect */
+ ssc = kzalloc(sizeof(*ssc), GFP_NOFS);
+ if (unlikely(!ssc))
+ goto out;
+
+ /* replace port '.' with '-' */
+ portstr = strrchr(src->na_uaddr, '.');
+ if (!portstr) {
+ dprintk("NFS: %s: Failed finding expected dot in port\n",
+ __func__);
+ goto out_free_ssc;
+ }
+ *portstr = '-';
+
+ /* find '.' between address and port */
+ portstr = strrchr(src->na_uaddr, '.');
+ if (!portstr) {
+ dprintk("NFS: %s:Failed finding expected dot between address"
+ " and port \n", __func__);
+ goto out_free_ssc;
+ }
+ *portstr = '\0';
+
+ if (!rpc_pton(net, src->na_uaddr, portstr-src->na_uaddr,
+ (struct sockaddr *)&ssc->sc_addr, sizeof(ssc->sc_addr))) {
+ dprintk("NFS: %s: error parsing address: %s\n",
+ __func__, src->na_uaddr);
+ goto out_free_ssc;
+ }
+
+ portstr++;
+ sscanf(portstr, "%d-%d", &tmp[0], &tmp[1]);
+ port = htons((tmp[0] << 8) | (tmp[1]));
+
+ switch (ssc->sc_addr.ss_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)&ssc->sc_addr)->sin_port = port;
+ ssc->sc_addrlen = sizeof(struct sockaddr_in);
+ match_netid = "tcp";
+ match_netid_len = 3;
+ break;
+
+ case AF_INET6:
+ ((struct sockaddr_in6 *)&ssc->sc_addr)->sin6_port = port;
+ ssc->sc_addrlen = sizeof(struct sockaddr_in6);
+ match_netid = "tcp6";
+ match_netid_len = 4;
+ startsep = "[";
+ endsep = "]";
+ break;
+
+ default:
+ dprintk("%s: unsupported address family: %u\n",
+ __func__, ssc->sc_addr.ss_family);
+ goto out_free_ssc;
+ }
+
+ if (src->na_netid_len != match_netid_len ||
+ strncmp(src->na_netid, match_netid, src->na_netid_len)) {
+ dprintk("NFS: %s: ERROR: netid \"%s\" != \"%s\"\n",
+ __func__, src->na_netid, match_netid);
+ goto out_free_ssc;
+ }
+
+ len = strlen(startsep) + strlen(src->na_uaddr) + strlen(endsep) + 9;
+ /* Freed in ssc_disconnect */
+ ssc->sc_ipaddr = kzalloc(len, GFP_NOFS);
+ if (!ssc->sc_ipaddr)
+ goto out_free_ssc;
+
+ snprintf(ssc->sc_ipaddr, len, "%s%s%s", startsep, src->na_uaddr,
+ endsep);
+
+ dprintk("%s sc_ipaddr %s\n", __func__, ssc->sc_ipaddr);
+
+ status = nfs4_make_ssc_rawdata(ssc->sc_ipaddr, clientip, &raw_data);
+ if (status < 0)
+ goto out_free_ipaddr;
+
+ snprintf(ssc->sc_ipaddr, len, "%s:/", ssc->sc_ipaddr);
+
+ /**
+ * Create a dev_name: "source server ip address:/"
+ * ssc->sc_addr should resolve to server.domain.
+ * need export path.
+ */
+ mnt_dentry = nfs4_ssc_mount(0, ssc->sc_ipaddr, raw_data);
+ if (IS_ERR(mnt_dentry)) {
+ printk("%s nfs4_ssc_mount returns %ld\n", __func__,
+ PTR_ERR(mnt_dentry));
+ goto out_free_ipaddr;
+ }
+
+ spin_lock(&nfs4_s2s_cache_lock);
+ tmp_ssc = _ssc_lookup_locked(ssc->sc_ipaddr);
+ if (tmp_ssc == NULL) {
+ INIT_LIST_HEAD(&ssc->sc_node);
+ list_add(&ssc->sc_node, &nfs4_s2s_client_cache);
+ dprintk("%s add NEW ssc node %s\n", __func__, ssc->sc_ipaddr);
+
+ } else {
+ kfree(ssc->sc_ipaddr);
+ kfree(ssc);
+ ssc = tmp_ssc;
+ dprintk("%s FOUND existing ssc node %s\n", __func__,
+ ssc->sc_ipaddr);
+ }
+ spin_unlock(&nfs4_s2s_cache_lock);
+
+ ssc->sc_mnt_dentry = mnt_dentry;
+ /* Hard wire version 4 */
+ ssc->sc_root_mnt = nfs_get_root_mnt(4, raw_data);
+ return ssc;
+
+out_free_ipaddr:
+ kfree(ssc->sc_ipaddr);
+out_free_ssc:
+ kfree(ssc);
+out:
+ return NULL;
+}
+
+static void
+nfs4_raw_2_fh(struct nfs_fh *fh, u32 fh_sz, char *fh_data)
+{
+ BUG_ON(fh_sz > NFS_MAXFHSIZE);
+
+ fh->size = (unsigned short)fh_sz;
+ memcpy(fh->data, fh_data, fh_sz);
+}
+
+static void
+nfs4_raw_2_stid(nfs4_stateid *stid, u32 st_seqid, char *st_opaque)
+{
+ stid->seqid = st_seqid;
+ memcpy(stid->other, st_opaque, NFS4_STATEID_OTHER_SIZE);
+}
+
+/**
+ * clean up and umount. Note: read dentry (filep->path.dentry) has
+ * been dput for the READ, should have a d_count of 1 entering this
+ * function.
+ */
+static void
+nfs4_ssc_disconnect(struct nfs42_ssc_client *ssc, struct file *filep)
+{
+ struct super_block *sb;
+ struct inode *inode = filep->f_inode;
+ int res;
+
+ dprintk("--> %s dentry %p d_count %d f_count %ld mnt_d %p dcount %d\n",
+ __func__, filep->f_path.dentry, d_count(filep->f_path.dentry),
+ atomic_long_read(&filep->f_count), ssc->sc_mnt_dentry,
+ d_count(ssc->sc_mnt_dentry));
+
+ sb = ssc->sc_mnt_dentry->d_inode->i_sb;
+
+ res = nfs_file_release(inode, filep);
+
+ /* Needs dcount of zero */
+ dput(ssc->sc_mnt_dentry);
+
+ nfs_umount_begin(sb);
+ nfs_kill_super(sb);
+
+ /* free ssc (ssc->sc_ipaddr too) */
+ kfree(ssc->sc_ipaddr);
+ kfree(ssc);
+}
+
+static struct file *
+nfs4_ssc_open(struct nfs42_ssc_client *ssc, u32 fh_sz, char *fh_data,
+ u32 st_seqid, char *st_opaque)
+{
+ struct nfs_fattr fattr;
+ struct path path = {
+ .dentry = NULL,
+ };
+ struct qstr fname;
+ struct file *filep = NULL;
+ struct nfs_server *server;
+ struct nfs_fh src_fh;
+ struct inode *r_ino = NULL;
+ struct nfs_open_context *ctx;
+ struct nfs4_state_owner *sp;
+ nfs4_stateid stateid;
+ char read_name[SSC_READ_NAME_LEN];
+ int status = 0;
+
+ dprintk("--> %s ssc %p sc_mnt_dentry %p d_inode %p\n", __func__,
+ ssc, ssc ? ssc->sc_mnt_dentry : NULL,
+ ssc->sc_mnt_dentry ? ssc->sc_mnt_dentry->d_inode : NULL);
+
+ /* vfsmount is bad for some reason */
+ if (IS_ERR(ssc->sc_root_mnt)) {
+ dprintk("%s MOUNT ERROR %ld\n", __func__,
+ PTR_ERR(ssc->sc_root_mnt));
+ filep = ERR_CAST(ssc->sc_root_mnt);
+ goto out;
+ }
+ server = NFS_SERVER(ssc->sc_mnt_dentry->d_inode);
+
+ nfs4_raw_2_fh(&src_fh, fh_sz, fh_data);
+ nfs4_raw_2_stid(&stateid, st_seqid, st_opaque);
+ nfs_fattr_init(&fattr);
+
+ status = nfs4_proc_getattr(server, &src_fh, &fattr, NULL);
+ if (status < 0) {
+ dprintk("%s nfs4_proc_getattr error %d\n", __func__, status);
+ filep = ERR_PTR(status);
+ goto out;
+ }
+ fname.len = snprintf(read_name, SSC_READ_NAME_LEN, SSC_READ_NAME_BODY,
+ read_name_gen++);
+
+ fname.name = read_name;
+ fname.hash = full_name_hash(read_name, fname.len);
+
+ /* Just put the file under the mount point */
+ path.dentry = d_alloc(ssc->sc_mnt_dentry, &fname);
+ if (path.dentry == NULL)
+ goto out;
+ dprintk("%s ssc->sc_root_mnt %p\n", __func__, ssc->sc_root_mnt);
+ path.mnt = ssc->sc_root_mnt;
+
+ r_ino = nfs_fhget(ssc->sc_mnt_dentry->d_inode->i_sb, &src_fh, &fattr,
+ NULL);
+
+ d_instantiate(path.dentry, r_ino);
+ d_count(path.dentry);
+
+ /* Here is why we need to expose the vfsmount via nfs_fs_mount */
+ filep = alloc_file(&path, FMODE_READ, r_ino->i_fop);
+ if (IS_ERR(filep))
+ goto out_path;
+
+ ctx = alloc_nfs_open_context(filep->f_path.dentry, filep->f_mode);
+ if (IS_ERR(ctx))
+ goto out_filep;
+
+ sp = nfs4_get_state_owner(server, ctx->cred, GFP_KERNEL);
+ if (sp == NULL)
+ goto out_ctx;
+
+ ctx->state = nfs4_get_open_state(r_ino, sp);
+ if (ctx->state == NULL)
+ goto out_stateowner;
+
+ __update_open_stateid(ctx->state, &stateid, NULL, filep->f_mode);
+
+ nfs_file_set_open_context(filep, ctx);
+ put_nfs_open_context(ctx); /* nfs_open does this.. :) */
+out:
+ dprintk("<-- %s error %ld filep %p r_ino %p\n", __func__,
+ IS_ERR(filep) ? PTR_ERR(filep) : 0,
+ filep, r_ino);
+ return filep;
+
+out_stateowner:
+ nfs4_put_state_owner(sp);
+
+out_ctx:
+ put_nfs_open_context(ctx);
+
+out_filep:
+ fput(filep);
+
+out_path:
+ path_put(&path);
+ goto out;
+}
+
+static struct nfs42_inter_ssc_ops ssc_ops = {
+ .ssc_version = 42,
+ .ssc_name = "INTER_SSC",
+ .ssc_owner = THIS_MODULE,
+ .ssc_connect = &nfs4_ssc_connect,
+ .ssc_disconnect = &nfs4_ssc_disconnect,
+ .ssc_open = &nfs4_ssc_open,
+};
+
+
+static int __init nfs4intercopy_init(void)
+{
+ printk(KERN_INFO "%s: NFSv4 Inter SSC Driver Registering...\n",
+ __func__);
+ read_name_gen = 0;
+ return nfsd4_register_intecopy_driver(&ssc_ops, 42);
+}
+
+static void __exit nfs4intercopy_exit(void)
+{
+ printk(KERN_INFO "%s: NFSv4 Inter SSC Driver Unregistering...\n",
+ __func__);
+ nfsd4_unregister_intecopy_driver(&ssc_ops);
+}
+
+MODULE_ALIAS("nfs42-interserver-copy");
+
+module_init(nfs4intercopy_init);
+module_exit(nfs4intercopy_exit);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 511a422..d4b146b 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -83,7 +83,6 @@ static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *, long *);
static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
-static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *label);
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label);
static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
struct nfs_fattr *fattr, struct iattr *sattr,
@@ -1271,7 +1270,7 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *
nfs4_stateid_copy(&state->open_stateid, stateid);
}

-static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
+void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
{
/*
* Protect the call to nfs4_state_set_mode_locked and
@@ -1289,6 +1288,7 @@ static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_s
update_open_stateflags(state, fmode);
spin_unlock(&state->owner->so_lock);
}
+EXPORT_SYMBOL_GPL(__update_open_stateid); /* Ugly, but use it for inter ssc */

static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
{
@@ -3223,7 +3223,7 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
}

-static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr, struct nfs4_label *label)
{
struct nfs4_exception exception = { };
@@ -3236,6 +3236,7 @@ static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
} while (exception.retry);
return err;
}
+EXPORT_SYMBOL_GPL(nfs4_proc_getattr);

/*
* The file is not closed if it is opened due to the a request to change
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index f95e3b5..c47acf6 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -579,6 +579,7 @@ out:
nfs4_gc_state_owners(server);
return sp;
}
+EXPORT_SYMBOL_GPL(nfs4_get_state_owner); /* Ug. for inter SSC */

/**
* nfs4_put_state_owner - Release a nfs4_state_owner
@@ -604,6 +605,7 @@ void nfs4_put_state_owner(struct nfs4_state_owner *sp)
list_add_tail(&sp->so_lru, &server->state_owners_lru);
spin_unlock(&clp->cl_lock);
}
+EXPORT_SYMBOL_GPL(nfs4_put_state_owner); /* Ug for INTER SSC */

/**
* nfs4_purge_state_owners - Release all cached state owners
@@ -720,6 +722,7 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
out:
return state;
}
+EXPORT_SYMBOL_GPL(nfs4_get_open_state); /* Ug. for INTER SSC */

void nfs4_put_open_state(struct nfs4_state *state)
{
diff --git a/include/linux/nfs4intercopy.h b/include/linux/nfs4intercopy.h
new file mode 100644
index 0000000..48f660f
--- /dev/null
+++ b/include/linux/nfs4intercopy.h
@@ -0,0 +1,44 @@
+/*
+ * linux/fs/nfs/nfs4intercopy.h
+ *
+ * Copyright (C) 2014 Andy Adamson <[email protected]>
+ *
+ * nfs inter-server server-side copy READ implementation
+ *
+ */
+
+struct nfs42_netaddr {
+ unsigned int na_netid_len;
+ char na_netid[RPCBIND_MAXNETIDLEN + 1];
+ unsigned int na_uaddr_len;
+ char na_uaddr[RPCBIND_MAXUADDRLEN + 1];
+};
+
+struct nfs42_ssc_client {
+ struct list_head sc_node;
+ char *sc_ipaddr;
+ struct dentry *sc_mnt_dentry;
+ struct sockaddr_storage sc_addr;
+ size_t sc_addrlen;
+ struct vfsmount *sc_root_mnt;
+};
+
+
+struct nfs42_inter_ssc_ops {
+ struct list_head ssc_mtable;
+ u32 ssc_version; /* version of nfs */
+ const char *ssc_name;
+ struct module *ssc_owner;
+
+ /* test for nfs page cache coalescing */
+ const struct nfs_pageio_ops *ssc_pg_read_ops;
+
+ struct nfs42_ssc_client *(*ssc_connect)(struct nfs42_netaddr *src, struct net *net, char *clientip);
+ void(*ssc_disconnect)(struct nfs42_ssc_client *ssc, struct file *filep);
+ struct file *(*ssc_open)(struct nfs42_ssc_client *ssc, u32 fh_sz, char *fh_data, u32 st_seqid, char *st_opaque);
+};
+
+extern int nfsd4_register_intecopy_driver(struct nfs42_inter_ssc_ops *, u32);
+extern void nfsd4_unregister_intecopy_driver(struct nfs42_inter_ssc_ops *);
+extern void set_ssc_module(struct nfs42_inter_ssc_ops **, u32);
+extern void unset_ssc_module(struct nfs42_inter_ssc_ops *);
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 2f2bf3d..83c16c9 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1264,6 +1264,9 @@ nfs_free_pnfs_ds_cinfo(struct pnfs_ds_commit_info *cinfo)
#endif /* CONFIG_NFS_V4_1 */

#ifdef CONFIG_NFS_V4_2
+
+#include <linux/nfs4intercopy.h>
+
struct nfs42_falloc_args {
struct nfs4_sequence_args seq_args;

--
1.8.3.1


2015-03-17 22:41:13

by Andy Adamson

[permalink] [raw]
Subject: [PATCH RFC 01/10] NFS return the root_mnt via the raw data in nfs_fs_mount

From: Andy Adamson <[email protected]>

We need the vfsmount to construct the NFS READ request from the
destination server to the source server.

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfs/internal.h | 2 ++
fs/nfs/nfs4super.c | 2 ++
fs/nfs/super.c | 27 +++++++++++++++++++++++++++
include/uapi/linux/nfs4_mount.h | 1 +
4 files changed, 32 insertions(+)

diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 9e6475b..473e241 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -115,6 +115,7 @@ struct nfs_parsed_mount_data {

struct security_mnt_opts lsm_opts;
struct net *net;
+ struct vfsmount *root_mnt;
};

/* mount_clnt.c */
@@ -373,6 +374,7 @@ extern struct file_system_type nfs_xdev_fs_type;
extern struct file_system_type nfs4_xdev_fs_type;
extern struct file_system_type nfs4_referral_fs_type;
#endif
+extern struct vfsmount *nfs_get_root_mnt(int vers, char *raw_data);
bool nfs_auth_info_match(const struct nfs_auth_info *, rpc_authflavor_t);
struct dentry *nfs_try_mount(int, const char *, struct nfs_mount_info *,
struct nfs_subversion *);
diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c
index 75090fe..d2c1883 100644
--- a/fs/nfs/nfs4super.c
+++ b/fs/nfs/nfs4super.c
@@ -252,6 +252,8 @@ struct dentry *nfs4_try_mount(int flags, const char *dev_name,

res = nfs_follow_remote_path(root_mnt, export_path);

+ data->root_mnt = root_mnt;
+
dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n",
PTR_ERR_OR_ZERO(res),
IS_ERR(res) ? " [error]" : "");
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 322b2de02..1b61e58 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2608,6 +2608,32 @@ error_splat_super:
}
EXPORT_SYMBOL_GPL(nfs_fs_mount_common);

+static void nfs_set_root_mnt(struct file_system_type *fs_type, void *raw_data,
+ struct nfs_parsed_mount_data *pdata)
+{
+ if (fs_type == &nfs4_fs_type) {
+ struct nfs4_mount_data *data =
+ (struct nfs4_mount_data *)raw_data;
+ dprintk("%s pdata->root_mnt %p\n", __func__, pdata->root_mnt);
+ data->root_mnt = pdata->root_mnt;
+ }
+}
+
+struct vfsmount *nfs_get_root_mnt(int vers, char *raw_data)
+{
+ struct vfsmount *mnt = ERR_PTR(-EINVAL);
+
+ if (vers == 4) {
+ struct nfs4_mount_data *data =
+ (struct nfs4_mount_data *)raw_data;
+
+ dprintk("%s data->root_mnt %p\n", __func__, data->root_mnt);
+ mnt = data->root_mnt;
+ }
+ return mnt;
+}
+EXPORT_SYMBOL_GPL(nfs_get_root_mnt);
+
struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data)
{
@@ -2640,6 +2666,7 @@ struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
}

mntroot = nfs_mod->rpc_ops->try_mount(flags, dev_name, &mount_info, nfs_mod);
+ nfs_set_root_mnt(nfs_mod->nfs_fs, raw_data, mount_info.parsed);

put_nfs_version(nfs_mod);
out:
diff --git a/include/uapi/linux/nfs4_mount.h b/include/uapi/linux/nfs4_mount.h
index a0dcf66..91c9539 100644
--- a/include/uapi/linux/nfs4_mount.h
+++ b/include/uapi/linux/nfs4_mount.h
@@ -53,6 +53,7 @@ struct nfs4_mount_data {
/* Pseudo-flavours to use for authentication. See RFC2623 */
int auth_flavourlen; /* 1 */
int __user *auth_flavours; /* 1 */
+ struct vfsmount *root_mnt; /* 2 */
};

/* bits in the flags field */
--
1.8.3.1


2015-03-17 22:41:11

by Andy Adamson

[permalink] [raw]
Subject: [PATCH RFC 03/10] NFS add COPY_NOTIFY operation

From: Andy Adamson <[email protected]>

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

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfs/nfs42.h | 2 +
fs/nfs/nfs42proc.c | 57 ++++++++++++++++
fs/nfs/nfs42xdr.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++
fs/nfs/nfs4file.c | 30 +++++++--
fs/nfs/nfs4proc.c | 1 +
fs/nfs/nfs4xdr.c | 1 +
include/linux/nfs4.h | 7 ++
include/linux/nfs_fs_sb.h | 1 +
include/linux/nfs_xdr.h | 38 +++++++++++
9 files changed, 299 insertions(+), 4 deletions(-)

diff --git a/fs/nfs/nfs42.h b/fs/nfs/nfs42.h
index 28da8dc..849b51e 100644
--- a/fs/nfs/nfs42.h
+++ b/fs/nfs/nfs42.h
@@ -8,6 +8,8 @@
/* nfs4.2proc.c */
int nfs42_proc_allocate(struct file *, loff_t, loff_t);
ssize_t nfs42_proc_copy(struct file *, loff_t, struct file *, loff_t, size_t);
+int nfs42_proc_copy_notify(struct file *, struct file *,
+ struct nfs42_copy_notify_res *);
int nfs42_proc_deallocate(struct file *, loff_t, loff_t);
loff_t nfs42_proc_llseek(struct file *, loff_t, int);

diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index 1ce9274..e09e793 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -11,6 +11,8 @@
#include "nfs4_fs.h"
#include "nfs42.h"

+#define NFSDBG_FACILITY NFSDBG_PROC
+
static int nfs42_set_rw_stateid(nfs4_stateid *dst, struct file *file,
fmode_t fmode)
{
@@ -32,6 +34,28 @@ static int nfs42_set_rw_stateid(nfs4_stateid *dst, struct file *file,
return ret;
}

+static void nfs42_set_cn_args_netaddr(struct file *file_out,
+ struct nfs42_netaddr *naddr)
+{
+ struct nfs_client *clp = (NFS_SERVER(file_inode(file_out)))->nfs_client;
+ unsigned short port = 2049;
+
+ rcu_read_lock();
+ naddr->na_netid_len = scnprintf(naddr->na_netid,
+ sizeof(naddr->na_netid), "%s",
+ rpc_peeraddr2str(clp->cl_rpcclient,
+ RPC_DISPLAY_NETID));
+ naddr->na_uaddr_len = scnprintf(naddr->na_uaddr,
+ sizeof(naddr->na_uaddr),
+ "%s.%u.%u",
+ rpc_peeraddr2str(clp->cl_rpcclient,
+ RPC_DISPLAY_ADDR),
+ port >> 8, port & 255);
+ rcu_read_unlock();
+ dprintk("<-- %s netid %s uaddr %s\n", __func__,
+ naddr->na_netid, naddr->na_uaddr);
+}
+
static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
loff_t offset, loff_t len)
{
@@ -162,6 +186,39 @@ ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
return res.write_res.count;
}

+int nfs42_proc_copy_notify(struct file *src, struct file *dst,
+ struct nfs42_copy_notify_res *res)
+{
+ struct nfs42_copy_notify_args args = {
+ .cna_src_fh = NFS_FH(file_inode(src)),
+ };
+ 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,
+ };
+ struct nfs_server *server = NFS_SERVER(file_inode(src));
+ int status;
+
+ if (!(server->caps & NFS_CAP_COPY_NOTIFY))
+ return -ENOTSUPP;
+
+ args.cna_nl_type = NL4_NETADDR;
+ nfs42_set_cn_args_netaddr(src, &args.u.cna_addr);
+
+ status = nfs42_set_rw_stateid(&args.cna_src_stateid, src, FMODE_READ);
+ 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)
+ server->caps &= ~NFS_CAP_COPY_NOTIFY;
+
+ return status;
+}
+
loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
{
struct inode *inode = file_inode(filep);
diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c
index f4b301c..94a484a 100644
--- a/fs/nfs/nfs42xdr.c
+++ b/fs/nfs/nfs42xdr.c
@@ -23,6 +23,16 @@
NFS42_WRITE_RES_SIZE + \
1 /* cr_consecutive */ + \
1 /* cr_synchronous */)
+#define encode_copy_notify_maxsz (op_encode_hdr_maxsz + \
+ XDR_QUADLEN(NFS4_STATEID_SIZE) + \
+ 1 + /* nl4_type */ \
+ 1 + XDR_QUADLEN(NFS4_OPAQUE_LIMIT))
+#define decode_copy_notify_maxsz (op_decode_hdr_maxsz + \
+ 3 + /* cnr_lease_time */\
+ XDR_QUADLEN(NFS4_STATEID_SIZE) + \
+ 1 + /* Support 1 cnr_source_server */\
+ 1 + /* nl4_type */ \
+ 1 + XDR_QUADLEN(NFS4_OPAQUE_LIMIT))
#define encode_deallocate_maxsz (op_encode_hdr_maxsz + \
encode_fallocate_maxsz)
#define decode_deallocate_maxsz (op_decode_hdr_maxsz)
@@ -63,6 +73,12 @@
decode_savefh_maxsz + \
decode_putfh_maxsz + \
decode_copy_maxsz)
+#define NFS4_enc_copy_notify_sz (compound_encode_hdr_maxsz + \
+ encode_putfh_maxsz + \
+ encode_copy_notify_maxsz)
+#define NFS4_dec_copy_notify_sz (compound_decode_hdr_maxsz + \
+ decode_putfh_maxsz + \
+ decode_copy_notify_maxsz)
#define NFS4_enc_deallocate_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_deallocate_maxsz + \
@@ -118,6 +134,27 @@ static void encode_copy(struct xdr_stream *xdr,
encode_uint32(xdr, 0); /* src server list */
}

+static void encode_copy_notify(struct xdr_stream *xdr,
+ struct nfs42_copy_notify_args *args,
+ struct compound_hdr *hdr)
+{
+ encode_op_hdr(xdr, OP_COPY_NOTIFY, decode_copy_notify_maxsz, hdr);
+ encode_nfs4_stateid(xdr, &args->cna_src_stateid);
+ encode_uint32(xdr, args->cna_nl_type);
+ switch (args->cna_nl_type) {
+ case NL4_NAME:
+ case NL4_URL:
+ encode_string(xdr, args->u.cna_str_sz, args->u.cna_str);
+ break;
+ case NL4_NETADDR:
+ encode_string(xdr, args->u.cna_addr.na_netid_len, args->u.cna_addr.na_netid);
+ encode_string(xdr, args->u.cna_addr.na_uaddr_len, args->u.cna_addr.na_uaddr);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+}
+
static void encode_deallocate(struct xdr_stream *xdr,
struct nfs42_falloc_args *args,
struct compound_hdr *hdr)
@@ -186,6 +223,24 @@ static void nfs4_xdr_enc_copy(struct rpc_rqst *req,
}

/*
+ * Encode COPY_NOTIFY request
+ */
+static void nfs4_xdr_enc_copy_notify(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ struct nfs42_copy_notify_args *args)
+{
+ struct compound_hdr hdr = {
+ .minorversion = nfs4_xdr_minorversion(&args->cna_seq_args),
+ };
+
+ encode_compound_hdr(xdr, req, &hdr);
+ encode_sequence(xdr, &args->cna_seq_args, &hdr);
+ encode_putfh(xdr, args->cna_src_fh, &hdr);
+ encode_copy_notify(xdr, args, &hdr);
+ encode_nops(&hdr);
+}
+
+/*
* Encode DEALLOCATE request
*/
static void nfs4_xdr_enc_deallocate(struct rpc_rqst *req,
@@ -306,6 +361,92 @@ static int decode_copy(struct xdr_stream *xdr, struct nfs42_copy_res *res)
return decode_copy_requirements(xdr, res);
}

+static int decode_copy_notify(struct xdr_stream *xdr,
+ struct nfs42_copy_notify_res *res)
+{
+ __be32 *p;
+ uint32_t dummy;
+ char *dummy_str;
+ int status, i;
+
+ status = decode_op_hdr(xdr, OP_COPY_NOTIFY);
+ if (status)
+ return status;
+ /* cnr_lease_time */
+ p = xdr_inline_decode(xdr, 12);
+ if (unlikely(!p))
+ goto out_overflow;
+ p = xdr_decode_hyper(p, &res->cnr_lease_time.seconds);
+ res->cnr_lease_time.nseconds = be32_to_cpup(p);
+
+ status = decode_opaque_fixed(xdr, &res->cnr_stateid, NFS4_STATEID_SIZE);
+ if (unlikely(status))
+ goto out_overflow;
+
+ /* XXXX should be a decode_netaddr function to parse multiple
+ * addresses. For now, limit to one. */
+
+ /* number of source addresses */
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(!p))
+ goto out_overflow;
+ res->cnr_nsrc = be32_to_cpup(p);
+ if (res->cnr_nsrc > NFS42_MAX_SSC_SRC) {
+ pr_warn("NFS: %s num server > %d: %d. Exiting with error EIO\n",
+ __func__, NFS42_MAX_SSC_SRC, res->cnr_nsrc);
+ return -EIO;
+ }
+
+ for (i = 0; i < NFS42_MAX_SSC_SRC; i++) {
+ /* nl_type */
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(!p))
+ goto out_overflow;
+ res->src[i].cnr_nl_type = be32_to_cpup(p);
+ switch (res->src[i].cnr_nl_type) {
+ case NL4_NAME:
+ case NL4_URL:
+ status = decode_opaque_inline(xdr, &dummy, &dummy_str);
+ if (unlikely(status))
+ return status;
+ if (unlikely(dummy > NFS4_OPAQUE_LIMIT))
+ return -EIO;
+ memcpy(&res->src[i].u.cnr_str, dummy_str, dummy);
+ res->src[i].u.cnr_str_sz = dummy;
+ break;
+ case NL4_NETADDR:
+ /* netid string */
+ status = decode_opaque_inline(xdr, &dummy, &dummy_str);
+ if (unlikely(status))
+ return status;
+ if (unlikely(dummy > RPCBIND_MAXNETIDLEN))
+ return -EIO;
+ res->src[i].u.cnr_addr.na_netid_len = dummy;
+ memcpy(&res->src[i].u.cnr_addr.na_netid, dummy_str,
+ res->src[i].u.cnr_addr.na_netid_len);
+
+ /* uaddr string */
+ status = decode_opaque_inline(xdr, &dummy, &dummy_str);
+ if (unlikely(status))
+ return status;
+ if (unlikely(dummy > RPCBIND_MAXUADDRLEN))
+ return -EIO;
+ res->src[i].u.cnr_addr.na_uaddr_len = dummy;
+ memcpy(&res->src[i].u.cnr_addr.na_uaddr, dummy_str,
+ res->src[i].u.cnr_addr.na_uaddr_len);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EIO;
+ }
+ }
+ return 0;
+
+out_overflow:
+ print_overflow_msg(__func__, xdr);
+ return -EIO;
+}
+
static int decode_deallocate(struct xdr_stream *xdr, struct nfs42_falloc_res *res)
{
return decode_op_hdr(xdr, OP_DEALLOCATE);
@@ -481,6 +622,31 @@ out:
}

/*
+ * Decode COPY_NOTIFY response
+ */
+static int nfs4_xdr_dec_copy_notify(struct rpc_rqst *rqstp,
+ struct xdr_stream *xdr,
+ struct nfs42_copy_notify_res *res)
+{
+ struct compound_hdr hdr;
+ int status;
+
+ status = decode_compound_hdr(xdr, &hdr);
+ if (status)
+ goto out;
+ status = decode_sequence(xdr, &res->cnr_seq_res, rqstp);
+ if (status)
+ goto out;
+ status = decode_putfh(xdr);
+ if (status)
+ goto out;
+ status = decode_copy_notify(xdr, res);
+
+out:
+ return status;
+}
+
+/*
* Decode DEALLOCATE request
*/
static int nfs4_xdr_dec_deallocate(struct rpc_rqst *rqstp,
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index e3be9c33..c71bf6a 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -11,6 +11,7 @@
#include "pnfs.h"

#ifdef CONFIG_NFS_V4_2
+#include <linux/sunrpc/addr.h>
#include "nfs42.h"
#endif

@@ -122,14 +123,35 @@ nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
}

#ifdef CONFIG_NFS_V4_2
+static bool nfs42_intra_ssc(struct file *in, struct file *out)
+{
+ struct nfs_client *c_in = (NFS_SERVER(file_inode(in)))->nfs_client;
+ struct nfs_client *c_out = (NFS_SERVER(file_inode(out)))->nfs_client;
+
+ return rpc_cmp_addr((struct sockaddr *)&c_in->cl_addr,
+ (struct sockaddr *)&c_out->cl_addr);
+}
+
static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
size_t count, int flags)
{
- if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb ||
- file_in->f_path.mnt != file_out->f_path.mnt)
- return -ENOTSUPP;
- return nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count);
+ if (nfs42_intra_ssc(file_in, file_out)) { /* Intra-ssc */
+ if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb ||
+ file_in->f_path.mnt != file_out->f_path.mnt)
+ return -ENOTSUPP;
+ return nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count);
+ } else { /* Inter-ssc */
+ struct nfs42_copy_notify_res cn_res = {
+ .cnr_nsrc = 0,
+ };
+ int err;
+
+ err = nfs42_proc_copy_notify(file_in, file_out, &cn_res);
+ if (err)
+ return err;
+ return nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count);
+ }
}

static loff_t nfs4_file_llseek(struct file *filep, loff_t offset, int whence)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index d4b146b..d96219f 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -8575,6 +8575,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
| NFS_CAP_ATOMIC_OPEN_V1
| NFS_CAP_ALLOCATE
| NFS_CAP_COPY
+ | NFS_CAP_COPY_NOTIFY
| NFS_CAP_DEALLOCATE
| NFS_CAP_READ_PLUS
| NFS_CAP_SEEK,
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 784eec0..fae32fe 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -7427,6 +7427,7 @@ struct rpc_procinfo nfs4_procedures[] = {
PROC(DEALLOCATE, enc_deallocate, dec_deallocate),
PROC(READ_PLUS, enc_read_plus, dec_read_plus),
PROC(COPY, enc_copy, dec_copy),
+ PROC(COPY_NOTIFY, enc_copy_notify, dec_copy_notify),
#endif /* CONFIG_NFS_V4_2 */
};

diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 9f99f9a..2b4fd1c 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -495,6 +495,7 @@ enum {
NFSPROC4_CLNT_DEALLOCATE,
NFSPROC4_CLNT_READ_PLUS,
NFSPROC4_CLNT_COPY,
+ NFSPROC4_CLNT_COPY_NOTIFY,
};

/* nfs41 types */
@@ -565,4 +566,10 @@ enum data_content4 {
NFS4_CONTENT_HOLE = 1,
};

+enum netloc_type4 {
+ NL4_NAME = 1,
+ NL4_URL = 2,
+ NL4_NETADDR = 3,
+};
+
#endif
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 478baf1..d0d2403 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -239,5 +239,6 @@ struct nfs_server {
#define NFS_CAP_DEALLOCATE (1U << 21)
#define NFS_CAP_READ_PLUS (1U << 22)
#define NFS_CAP_COPY (1U << 23)
+#define NFS_CAP_COPY_NOTIFY (1U << 24)

#endif
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 83c16c9..984e0e6 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1285,6 +1285,9 @@ struct nfs42_falloc_res {
const struct nfs_server *falloc_server;
};

+/* support 1 source server for now */
+#define NFS42_MAX_SSC_SRC 1
+
struct nfs42_copy_args {
struct nfs4_sequence_args seq_args;

@@ -1312,6 +1315,41 @@ struct nfs42_copy_res {
bool synchronous;
};

+
+struct nfs42_copy_notify_args {
+ struct nfs4_sequence_args cna_seq_args;
+
+ struct nfs_fh *cna_src_fh;
+ nfs4_stateid cna_src_stateid;
+ union { /* cna_destiniation_server */
+ struct { /* NL4_NAME, NL4_URL */
+ int cna_str_sz;
+ char cna_str[NFS4_OPAQUE_LIMIT + 1];
+ };
+ struct nfs42_netaddr cna_addr; /* NL4_NETADDR */
+ } u;
+ enum netloc_type4 cna_nl_type;
+};
+
+struct nfs42_copy_notify_res {
+ struct nfs4_sequence_res cnr_seq_res;
+
+ struct nfstime4 cnr_lease_time;
+ nfs4_stateid cnr_stateid;
+ int cnr_nsrc; /* for now, always 1 */
+ struct { /* cnr_source_server<> */
+ enum netloc_type4 cnr_nl_type;
+ union {
+ struct {
+ /* NL4_NAME, NL4_URL */
+ int cnr_str_sz;
+ char cnr_str[NFS4_OPAQUE_LIMIT + 1];
+ };
+ struct nfs42_netaddr cnr_addr; /* NL4_NETADDR */
+ } u;
+ } src[NFS42_MAX_SSC_SRC];
+};
+
struct nfs42_seek_args {
struct nfs4_sequence_args seq_args;

--
1.8.3.1


2015-03-17 22:41:17

by Andy Adamson

[permalink] [raw]
Subject: [PATCH RFC 05/10] NFSD add COPY_NOTIFY operation

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfsd/nfs4proc.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++--
fs/nfsd/nfs4xdr.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
fs/nfsd/xdr4.h | 14 +++++++
3 files changed, 223 insertions(+), 6 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 89cb664..82240b6 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1077,6 +1077,93 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status;
}

+static int
+nfsd4_set_src_nl4_netaddr(struct svc_rqst *rqstp, struct nfsd4_addr *naddr)
+{
+ const struct sockaddr *addr = (struct sockaddr *)&rqstp->rq_daddr;
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)addr;
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)addr;
+ int uaddr_len = rqstp->rq_daddrlen + 4 + 1; /* port (4) and '\0' (1) */
+ unsigned short port;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ port = ntohs(sin->sin_port);
+ snprintf(naddr->na_uaddr_val, uaddr_len, "%pI4.%u.%u",
+ &sin->sin_addr, port >> 8, port & 255);
+ naddr->na_uaddr_len = strlen(naddr->na_uaddr_val);
+ dprintk("%s na_uaddr_val %s na_uaddr_len %d\n", __func__,
+ naddr->na_uaddr_val, naddr->na_uaddr_len);
+
+ /* note: buf size has room for trailing null space but
+ * netid_len does not include the trailing null space */
+ snprintf(naddr->na_netid_val, 4, "%s", "tcp");
+ naddr->na_netid_len = 3;
+ dprintk("%s na_netid %s na_netid_len %d\n", __func__,
+ naddr->na_netid_val, naddr->na_netid_len);
+ break;
+ case AF_INET6:
+ /* XXX check this case - never tested */
+ port = ntohs(sin6->sin6_port);
+ snprintf(naddr->na_uaddr_val, naddr->na_uaddr_len,
+ "%pI6.%u.%u", &sin6->sin6_addr, port >> 8, port & 255);
+ snprintf(naddr->na_netid_val, 5, "%s", "tcp6");
+ naddr->na_netid_len = 4;
+ break;
+ default:
+ dprintk("NFSD nfsd4_set_notify_src: unknown address type: %d",
+ addr->sa_family);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static __be32
+nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+ struct nfsd4_copy_notify *cp_notify)
+{
+ __be32 status;
+ struct file *src = NULL;
+ struct nfsd4_addr *naddr;
+
+ status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate,
+ &cstate->current_fh,
+ &cp_notify->cpn_src_stateid,
+ RD_STATE, &src);
+ if (status)
+ return status;
+
+ /* XXX Set lease time in cp_notify cpn_sec and cpn_nsec */
+
+ /** XXX Save cpn_src_statid, cpn_src, and any other returned source
+ * server addresses on which the source server is williing to accept
+ * connections from the destination e.g. what is returned in cpn_src,
+ * to verify READ from dest server.
+ */
+
+ /**
+ * For now, only return one source server address, the address used
+ * by the client in the static cpn_src.
+ */
+ cp_notify->cpn_nsrc = 1;
+ cp_notify->cpn_src.nl4_type = NL4_NETADDR;
+ naddr = &cp_notify->cpn_src.nl4_addr;
+
+ status = nfsd4_set_src_nl4_netaddr(rqstp, naddr);
+ if (status != 0)
+ goto out;
+
+ cp_notify->cpn_nsrc = 1;
+
+ dprintk("<-- %s cpn_dst %s:%s cpn_src %s:%s\n", __func__,
+ cp_notify->cpn_dst.nl4_addr.na_netid_val,
+ cp_notify->cpn_dst.nl4_addr.na_uaddr_val,
+ cp_notify->cpn_src.nl4_addr.na_netid_val,
+ cp_notify->cpn_src.nl4_addr.na_uaddr_val);
+out:
+ return status;
+}
+
static __be32
nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_fallocate *fallocate, int flags)
@@ -1826,10 +1913,6 @@ out:
op_encode_ace_maxsz)

#define op_encode_channel_attrs_maxsz (6 + 1 + 1)
-#define op_encode_nfsd4_addr_maxsz (2 /* the two lengths */ + \
- XDR_QUADLEN(RPCBIND_MAXNETIDLEN) + \
- XDR_QUADLEN(RPCBIND_MAXUADDRLEN))
-

static inline u32 nfsd4_only_status_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
{
@@ -2049,6 +2132,15 @@ static inline u32 nfsd4_copy_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1); /*nl4_loc + nl4_loc_sz */
}

+static inline u32 nfsd4_copy_notify_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
+{
+ return (op_encode_hdr_size + 3 + /* cnr_lease_time */\
+ 1 + /* We support one cnr_source_server */\
+ XDR_QUADLEN(NFS4_STATEID_SIZE) + /* cnr_stateid */ \
+ 1 + /* nl4_type */\
+ XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1); /*nl4_loc + nl4_loc_sz */
+}
+
static struct nfsd4_operation nfsd4_ops[] = {
[OP_ACCESS] = {
.op_func = (nfsd4op_func)nfsd4_access,
@@ -2391,6 +2483,12 @@ static struct nfsd4_operation nfsd4_ops[] = {
.op_func = (nfsd4op_func)nfsd4_seek,
.op_name = "OP_SEEK",
},
+ [OP_COPY_NOTIFY] = {
+ .op_func = (nfsd4op_func)nfsd4_copy_notify,
+ .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
+ .op_name = "OP_COPY_NOTIFY",
+ .op_rsize_bop = (nfsd4op_rsize)nfsd4_copy_notify_rsize,
+ },
};

int nfsd4_max_reply(struct svc_rqst *rqstp, struct nfsd4_op *op)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index d2d5e7f..afcb6c4 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1727,6 +1727,49 @@ intra:
}

static __be32
+nfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp,
+ struct nfsd4_copy_notify *cp_notify)
+{
+ DECODE_HEAD;
+
+ status = nfsd4_decode_stateid(argp, &cp_notify->cpn_src_stateid);
+ if (status)
+ return status;
+
+ READ_BUF(4);
+ cp_notify->cpn_dst.nl4_type = be32_to_cpup(p++);
+ switch (cp_notify->cpn_dst.nl4_type) {
+ case NL4_NAME:
+ case NL4_URL:
+ READ_BUF(4);
+ cp_notify->cpn_dst.nl4_loc_sz = be32_to_cpup(p++);
+ SAVEMEM(cp_notify->cpn_dst.nl4_loc, cp_notify->cpn_dst.nl4_loc_sz);
+ break;
+ case NL4_NETADDR:
+ READ_BUF(4);
+ cp_notify->cpn_dst.nl4_addr.na_netid_len = be32_to_cpup(p++);
+ if (cp_notify->cpn_dst.nl4_addr.na_netid_len > RPCBIND_MAXNETIDLEN + 1)
+ goto xdr_error;
+ /* 4 for uaddr len */
+ READ_BUF(cp_notify->cpn_dst.nl4_addr.na_netid_len + 4);
+ COPYMEM(cp_notify->cpn_dst.nl4_addr.na_netid_val,
+ cp_notify->cpn_dst.nl4_addr.na_netid_len);
+
+ cp_notify->cpn_dst.nl4_addr.na_uaddr_len = be32_to_cpup(p++);
+ if (cp_notify->cpn_dst.nl4_addr.na_uaddr_len > RPCBIND_MAXUADDRLEN + 1)
+ goto xdr_error;
+ READ_BUF(cp_notify->cpn_dst.nl4_addr.na_uaddr_len);
+ COPYMEM(cp_notify->cpn_dst.nl4_addr.na_uaddr_val,
+ cp_notify->cpn_dst.nl4_addr.na_uaddr_len);
+ break;
+ default:
+ goto xdr_error;
+ }
+
+ DECODE_TAIL;
+}
+
+static __be32
nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
{
DECODE_HEAD;
@@ -1827,7 +1870,7 @@ static nfsd4_dec nfsd4_dec_ops[] = {
/* new operations for NFSv4.2 */
[OP_ALLOCATE] = (nfsd4_dec)nfsd4_decode_fallocate,
[OP_COPY] = (nfsd4_dec)nfsd4_decode_copy,
- [OP_COPY_NOTIFY] = (nfsd4_dec)nfsd4_decode_notsupp,
+ [OP_COPY_NOTIFY] = (nfsd4_dec)nfsd4_decode_copy_notify,
[OP_DEALLOCATE] = (nfsd4_dec)nfsd4_decode_fallocate,
[OP_IO_ADVISE] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_LAYOUTERROR] = (nfsd4_dec)nfsd4_decode_notsupp,
@@ -4331,6 +4374,68 @@ err_truncate:
}

static __be32
+nfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr,
+ struct nfsd4_copy_notify *cp_notify)
+{
+ struct xdr_stream *xdr = &resp->xdr;
+ struct nfsd4_addr *addr;
+ __be32 *p;
+
+ if (nfserr)
+ return nfserr;
+
+ /* 8 sec, 4 nsec, 4 cpn_num_src */
+ p = xdr_reserve_space(xdr, 16);
+ if (!p)
+ return nfserr_resource;
+
+ /* cnr_lease_time */
+ p = xdr_encode_hyper(p, cp_notify->cpn_sec);
+ *p++ = cpu_to_be32(cp_notify->cpn_nsec);
+
+ /* cnr_stateid */
+ nfserr = nfsd4_encode_stateid(xdr, &cp_notify->cp_stateid);
+ if (nfserr)
+ return nfserr;
+
+ *p++ = cpu_to_be32(cp_notify->cpn_nsrc); /* set to 1 */
+
+ /* cnr_source_servers <>: encode a single NL4_NETADDR src address */
+ p = xdr_reserve_space(xdr, 4);
+ *p++ = cpu_to_be32(cp_notify->cpn_src.nl4_type);
+
+ switch (cp_notify->cpn_src.nl4_type) {
+ case NL4_NAME:
+ case NL4_URL:
+ p = xdr_reserve_space(xdr, 4 + XDR_QUADLEN(cp_notify->cpn_src.nl4_loc_sz));
+ if (!p)
+ return nfserr_resource;
+ *p++ = cpu_to_be32(cp_notify->cpn_src.nl4_loc_sz);
+ p = xdr_encode_opaque_fixed(p, cp_notify->cpn_src.nl4_loc,
+ cp_notify->cpn_src.nl4_loc_sz);
+ break;
+ case NL4_NETADDR:
+ addr = &cp_notify->cpn_src.nl4_addr;
+
+ /* netid_len, max netid, uaddr_len, max uaddr + 4(port) */
+ p = xdr_reserve_space(xdr, 4 + XDR_QUADLEN(RPCBIND_MAXNETIDLEN)
+ + 4 + XDR_QUADLEN(RPCBIND_MAXUADDRLEN) + 4);
+ if (!p)
+ return nfserr_resource;
+
+ *p++ = cpu_to_be32(addr->na_netid_len);
+ p = xdr_encode_opaque_fixed(p, addr->na_netid_val, addr->na_netid_len);
+ *p++ = cpu_to_be32(addr->na_uaddr_len);
+ p = xdr_encode_opaque_fixed(p, addr->na_uaddr_val, addr->na_uaddr_len);
+ break;
+ default:
+ nfserr = nfserr_bad_xdr;
+ }
+
+ return nfserr;
+}
+
+static __be32
nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_seek *seek)
{
@@ -4430,7 +4535,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
/* NFSv4.2 operations */
[OP_ALLOCATE] = (nfsd4_enc)nfsd4_encode_noop,
[OP_COPY] = (nfsd4_enc)nfsd4_encode_copy,
- [OP_COPY_NOTIFY] = (nfsd4_enc)nfsd4_encode_noop,
+ [OP_COPY_NOTIFY] = (nfsd4_enc)nfsd4_encode_copy_notify,
[OP_DEALLOCATE] = (nfsd4_enc)nfsd4_encode_noop,
[OP_IO_ADVISE] = (nfsd4_enc)nfsd4_encode_noop,
[OP_LAYOUTERROR] = (nfsd4_enc)nfsd4_encode_noop,
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index be39051..97d80a6 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -539,6 +539,20 @@ struct nfsd4_seek {
loff_t seek_pos;
};

+struct nfsd4_copy_notify {
+ /* request */
+ stateid_t cpn_src_stateid;
+ struct nfsd4_nl4 cpn_dst;
+
+ /* response */
+ stateid_t cp_stateid;
+ u64 cpn_sec;
+ u32 cpn_nsec;
+ u32 cpn_nsrc;
+ /* one src sever address for now */
+ struct nfsd4_nl4 cpn_src;
+};
+
struct nfsd4_op {
int opnum;
__be32 status;
--
1.8.3.1


2015-03-17 22:41:22

by Andy Adamson

[permalink] [raw]
Subject: [PATCH RFC 10/10] NFSD nfs4 inter ssc copy

From: Andy Adamson <[email protected]>

registration of inter-ssc client module
call ssc_connect
call ssc_open
call scc_disconnect

Note: do not unload the nfs42-interserver-copy module at disconnect.

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfsd/nfs4proc.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 133 insertions(+), 8 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 5299631..0c21197 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -47,6 +47,8 @@
#include "pnfs.h"
#include "trace.h"

+#include <linux/nfs4intercopy.h>
+
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#include <linux/security.h>

@@ -1070,6 +1072,35 @@ nfsd4_verify_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status;
}

+/**
+ * ripped off from __svc_print_addr
+ */
+static void
+nfsd4_set_clientip(const struct sockaddr *addr, char *buf)
+{
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)addr;
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)addr;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ snprintf(buf, RPC_MAX_ADDRBUFLEN, "%pI4", &sin->sin_addr);
+ break;
+
+ case AF_INET6:
+ snprintf(buf, RPC_MAX_ADDRBUFLEN, "%pI6", &sin6->sin6_addr);
+ break;
+ }
+}
+
+static void
+nfsd4_addr_2_nfs42_netaddr(struct nfsd4_addr *src, struct nfs42_netaddr *dst)
+{
+ dst->na_netid_len = src->na_netid_len;
+ memcpy(dst->na_netid, src->na_netid_val, dst->na_netid_len);
+ dst->na_uaddr_len = src->na_uaddr_len;
+ memcpy(dst->na_uaddr, src->na_uaddr_val, dst->na_uaddr_len);
+}
+
static __be32
nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_copy *copy)
@@ -1077,16 +1108,90 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
ssize_t bytes;
__be32 status;
struct file *src = NULL, *dst = NULL;
+ char *fh_data = NULL, *st_data = NULL;
+ struct nfs42_inter_ssc_ops *issc_ops = NULL;
+ struct nfs42_ssc_client *sclp = NULL;

- status = nfsd4_verify_copy(rqstp, cstate, copy, &src, &dst);
- if (status)
- return status;
+ if (copy->cp_nsrc > 0) { /* Inter server SSC */
+ struct nfs42_netaddr naddr;
+ char clientip[RPC_MAX_ADDRBUFLEN] = {0,};
+ struct svc_fh *s_fh = NULL;
+ stateid_t *s_stid = &copy->cp_src_stateid;
+ u32 version = 42;

- /* Intra copy source fh is stale */
- if (HAS_CSTATE_FLAG(cstate, IS_STALE_FH)) {
+ dprintk("%s INTER SSC\n", __func__);
+
+ /* Only verify the destination stateid */
+ status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate,
+ &cstate->current_fh,
+ &copy->cp_dst_stateid,
+ WR_STATE, &dst);
+ if (status)
+ return status;
+
+ /* Inter copy source fh is always stale */
CLEAR_CSTATE_FLAG(cstate, IS_STALE_FH);
- cstate->status = nfserr_copy_stalefh;
- goto out;
+
+ status = -EINVAL;
+ /* Currently support for one NL4_NETADDR source server */
+ if (copy->cp_src.nl4_type != NL4_NETADDR) {
+ WARN(copy->cp_src.nl4_type != NL4_NETADDR,
+ "nfsd4_copy src server not NL4_NETADDR\n");
+ goto out;
+ }
+
+ set_ssc_module(&issc_ops, version);
+ dprintk("%s set_ssc_module issc_ops %p \n", __func__,
+ issc_ops);
+ if (issc_ops == NULL)
+ goto out;
+
+ nfsd4_set_clientip((const struct sockaddr *)&rqstp->rq_daddr,
+ clientip);
+
+ printk("%s clientip %s\n", __func__, clientip);
+ nfsd4_addr_2_nfs42_netaddr(&copy->cp_src.nl4_addr, &naddr);
+
+ sclp = issc_ops->ssc_connect(&naddr, SVC_NET(rqstp), clientip);
+ dprintk("%s sclp %p\n", __func__, sclp);
+ if (sclp == NULL)
+ goto out;
+
+ s_fh = &cstate->save_fh;
+ status = -ENOMEM;
+ fh_data = kzalloc(NFS_MAXFHSIZE, GFP_KERNEL);
+ if (fh_data == NULL)
+ goto out;
+ st_data = kzalloc(sizeof(stateid_opaque_t), GFP_KERNEL);
+ if (st_data == NULL) {
+ kfree(fh_data);
+ goto out;
+ }
+
+ status = 0;
+ memcpy(fh_data, &s_fh->fh_handle.fh_base,
+ s_fh->fh_handle.fh_size);
+ memcpy(st_data, (void *)&s_stid->si_opaque,
+ sizeof(stateid_opaque_t));
+
+ src = issc_ops->ssc_open(sclp,
+ s_fh->fh_handle.fh_size, fh_data,
+ s_stid->si_generation, st_data);
+
+ dprintk("%s FILEP src %p\n", __func__, src);
+ } else {
+ dprintk("%s INTRA SSC\n", __func__);
+
+ status = nfsd4_verify_copy(rqstp, cstate, copy, &src, &dst);
+ if (status)
+ return status;
+
+ /* Intra copy source fh is stale */
+ if (HAS_CSTATE_FLAG(cstate, IS_STALE_FH)) {
+ CLEAR_CSTATE_FLAG(cstate, IS_STALE_FH);
+ cstate->status = nfserr_copy_stalefh;
+ goto out;
+ }
}

bytes = nfsd_copy_range(src, copy->cp_src_pos,
@@ -1104,8 +1209,28 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs_ok;
}

- fput(src);
+ if (copy->cp_nsrc > 0) { /* Inter server SSC */
+
+ dprintk("%s BEFORE src f_count %ld d_cound %d\n", __func__,
+ atomic_long_read(&src->f_count),
+ d_count(src->f_path.dentry));
+
+ /* One for the READ */
+ dput(src->f_path.dentry);
+
+ if (issc_ops && sclp)
+ /* Frees sclp */
+ issc_ops->ssc_disconnect(sclp, src);
+
+ kfree(st_data);
+ kfree(fh_data);
+ } else { /* Intra server SSC */
+ fput(src); /* XXXX check when fput is needed */
+ }
+
+ /* XXX is this needed for Intra copy?
fput(dst);
+ */
out:
return status;
}
--
1.8.3.1


2015-03-17 22:41:23

by Andy Adamson

[permalink] [raw]
Subject: [PATCH RFC 04/10] NFSD add ca_source_server<> to COPY

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfsd/nfs4proc.c | 12 ++++++++++++
fs/nfsd/nfs4xdr.c | 45 +++++++++++++++++++++++++++++++++++++++++++--
fs/nfsd/xdr4.h | 16 ++++++++++++++++
3 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index c0340df..89cb664 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1826,6 +1826,10 @@ out:
op_encode_ace_maxsz)

#define op_encode_channel_attrs_maxsz (6 + 1 + 1)
+#define op_encode_nfsd4_addr_maxsz (2 /* the two lengths */ + \
+ XDR_QUADLEN(RPCBIND_MAXNETIDLEN) + \
+ XDR_QUADLEN(RPCBIND_MAXUADDRLEN))
+

static inline u32 nfsd4_only_status_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
{
@@ -2037,6 +2041,14 @@ static inline u32 nfsd4_layoutreturn_rsize(struct svc_rqst *rqstp, struct nfsd4_
}
#endif /* CONFIG_NFSD_PNFS */

+static inline u32 nfsd4_copy_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
+{
+ return (op_encode_hdr_size + op_encode_verifier_maxsz + \
+ 1 + /* One cnr_source_server */\
+ 1 + /* nl4_type */\
+ XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1); /*nl4_loc + nl4_loc_sz */
+}
+
static struct nfsd4_operation nfsd4_ops[] = {
[OP_ACCESS] = {
.op_func = (nfsd4op_func)nfsd4_access,
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 201a88e..d2d5e7f 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -39,6 +39,7 @@
#include <linux/utsname.h>
#include <linux/pagemap.h>
#include <linux/sunrpc/svcauth_gss.h>
+#include <linux/sunrpc/addr.h>

#include "idmap.h"
#include "acl.h"
@@ -1665,7 +1666,7 @@ static __be32
nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
{
DECODE_HEAD;
- unsigned int tmp;
+ struct nfsd4_addr *naddr;

status = nfsd4_decode_stateid(argp, &copy->cp_src_stateid);
if (status)
@@ -1680,8 +1681,48 @@ nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
p = xdr_decode_hyper(p, &copy->cp_count);
copy->cp_consecutive = be32_to_cpup(p++);
copy->cp_synchronous = be32_to_cpup(p++);
- tmp = *p++; /* Source server list not supported */
+ copy->cp_nsrc = be32_to_cpup(p++);

+ if (copy->cp_nsrc == 0) /* intra-server copy */
+ goto intra;
+
+ READ_BUF(4);
+ copy->cp_src.nl4_type = be32_to_cpup(p++);
+
+ /* currently support for 1 inter-server source server */
+ switch (copy->cp_src.nl4_type) {
+ case NL4_NAME:
+ case NL4_URL:
+ READ_BUF(4);
+ copy->cp_src.nl4_loc_sz = be32_to_cpup(p++);
+ if (copy->cp_src.nl4_loc_sz > NFS4_OPAQUE_LIMIT + 1)
+ goto xdr_error;
+
+ READ_BUF(copy->cp_src.nl4_loc_sz);
+ COPYMEM(copy->cp_src.nl4_loc, copy->cp_src.nl4_loc_sz);
+ break;
+ case NL4_NETADDR:
+ naddr = &copy->cp_src.nl4_addr;
+
+ READ_BUF(4);
+ naddr->na_netid_len = be32_to_cpup(p++);
+ if (naddr->na_netid_len > RPCBIND_MAXNETIDLEN + 1)
+ goto xdr_error;
+
+ READ_BUF(naddr->na_netid_len + 4); /* 4 for uaddr len */
+ COPYMEM(naddr->na_netid_val, naddr->na_netid_len);
+
+ naddr->na_uaddr_len = be32_to_cpup(p++);
+ if (naddr->na_uaddr_len > RPCBIND_MAXUADDRLEN + 1)
+ goto xdr_error;
+
+ READ_BUF(naddr->na_uaddr_len);
+ COPYMEM(naddr->na_uaddr_val, naddr->na_uaddr_len);
+ break;
+ default:
+ goto xdr_error;
+ }
+intra:
DECODE_TAIL;
}

diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 0e54e56..be39051 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -496,6 +496,20 @@ struct nfsd42_write_res {
nfs4_verifier wr_verifier;
};

+struct nfsd4_addr {
+ u32 na_netid_len;
+ char na_netid_val[RPCBIND_MAXNETIDLEN];
+ u32 na_uaddr_len;
+ char na_uaddr_val[RPCBIND_MAXUADDRLEN];
+};
+
+struct nfsd4_nl4 {
+ u32 nl4_type;
+ u32 nl4_loc_sz;
+ char *nl4_loc; /* NL4_NAME, NL4_URL */
+ struct nfsd4_addr nl4_addr; /* NL4_NETADDR */
+};
+
struct nfsd4_copy {
/* request */
stateid_t cp_src_stateid;
@@ -503,6 +517,8 @@ struct nfsd4_copy {
u64 cp_src_pos;
u64 cp_dst_pos;
u64 cp_count;
+ int cp_nsrc;
+ struct nfsd4_nl4 cp_src;

/* both */
bool cp_consecutive;
--
1.8.3.1


2015-03-17 22:41:24

by Andy Adamson

[permalink] [raw]
Subject: [PATCH RFC 09/10] NFSD: add nfs4interssc.c

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfsd/Makefile | 2 +-
fs/nfsd/nfs4interssc.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 111 insertions(+), 1 deletion(-)
create mode 100644 fs/nfsd/nfs4interssc.c

diff --git a/fs/nfsd/Makefile b/fs/nfsd/Makefile
index 9a6028e..6355e83 100644
--- a/fs/nfsd/Makefile
+++ b/fs/nfsd/Makefile
@@ -16,5 +16,5 @@ nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
- nfs4acl.o nfs4callback.o nfs4recover.o
+ nfs4acl.o nfs4callback.o nfs4recover.o nfs4interssc.o
nfsd-$(CONFIG_NFSD_PNFS) += nfs4layouts.o blocklayout.o blocklayoutxdr.o
diff --git a/fs/nfsd/nfs4interssc.c b/fs/nfsd/nfs4interssc.c
new file mode 100644
index 0000000..bdc73fc
--- /dev/null
+++ b/fs/nfsd/nfs4interssc.c
@@ -0,0 +1,110 @@
+/*
+ * linux/fs/nfsd/nfs4interssc.c
+ *
+ * Copyright (C) 2014 Andy Adamson <[email protected]>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/nfs_fs.h>
+
+
+/* Inter server side copy module list */
+static DEFINE_SPINLOCK(ssc_spinlock);
+static LIST_HEAD(ssc_modules_tbl);
+
+static struct nfs42_inter_ssc_ops *
+find_ssc_module_locked(int version) {
+ struct nfs42_inter_ssc_ops *local;
+
+ list_for_each_entry(local, &ssc_modules_tbl, ssc_mtable)
+ if (local->ssc_version == version)
+ goto out;
+ local = NULL;
+out:
+ printk("%s: Searching for version %u, found %p\n", __func__, version,
+ local);
+ return local;
+};
+
+static struct nfs42_inter_ssc_ops *
+find_ssc_module(u32 version)
+{
+ struct nfs42_inter_ssc_ops *local;
+
+ spin_lock(&ssc_spinlock);
+ local = find_ssc_module_locked(version);
+ if (local != NULL && !try_module_get(local->ssc_owner)) {
+ printk("%s: Could not grab reference on module\n", __func__);
+ local = NULL;
+ }
+ spin_unlock(&ssc_spinlock);
+ return local;
+}
+
+/**
+ * Ref counting for icopp?
+ */
+void
+set_ssc_module(struct nfs42_inter_ssc_ops **icopp, u32 version) {
+ struct nfs42_inter_ssc_ops *icop;
+
+ icop = find_ssc_module(version);
+ if (icop == NULL) {
+ request_module("nfs42-interserver-copy");
+ icop = find_ssc_module(version);
+ if (icop == NULL) {
+ printk("%s: No Module found for %u.\n",
+ __func__, version);
+ *icopp = NULL;
+ return;
+ }
+ }
+ *icopp = icop;
+ return;
+};
+
+void
+unset_ssc_module(struct nfs42_inter_ssc_ops *icop)
+{
+ printk("--> %s\n", __func__);
+ module_put(icop->ssc_owner);
+}
+
+int
+nfsd4_register_intecopy_driver(struct nfs42_inter_ssc_ops *ico, u32 version)
+{
+ struct nfs42_inter_ssc_ops *tmp;
+ int status = -EINVAL;
+
+ if (version != 42) {
+ printk(KERN_ERR "NFS: %s invalid module version %d\n",
+ __func__, version);
+ return status;
+ }
+
+ spin_lock(&ssc_spinlock);
+ tmp = find_ssc_module_locked(version);
+ if (tmp == NULL) {
+ list_add(&ico->ssc_mtable, &ssc_modules_tbl);
+ status = 0;
+ printk("%s Registering version:%u name:%s\n", __func__,
+ ico->ssc_version, ico->ssc_name);
+ } else {
+ printk(KERN_ERR "NFS: %s Module version %d already loaded!\n",
+ __func__, ico->ssc_version);
+ }
+ spin_unlock(&ssc_spinlock);
+ return status;
+}
+EXPORT_SYMBOL_GPL(nfsd4_register_intecopy_driver);
+
+void
+nfsd4_unregister_intecopy_driver(struct nfs42_inter_ssc_ops *ico)
+{
+ printk("%s Deregistering version:%u\n", __func__, ico->ssc_version);
+ spin_lock(&ssc_spinlock);
+ list_del(&ico->ssc_mtable);
+ spin_unlock(&ssc_spinlock);
+}
+EXPORT_SYMBOL_GPL(nfsd4_unregister_intecopy_driver);
--
1.8.3.1


2015-03-17 22:41:33

by Andy Adamson

[permalink] [raw]
Subject: [PATCH RFC 08/10] NFSD: allow inter server COPY to have a STALE source server fh

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfsd/nfs4proc.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
fs/nfsd/nfs4xdr.c | 26 ++++++++++++++++++++++++-
fs/nfsd/nfsd.h | 2 ++
fs/nfsd/xdr4.h | 4 ++++
4 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 7d899d2..5299631 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -35,6 +35,7 @@
#include <linux/file.h>
#include <linux/falloc.h>
#include <linux/slab.h>
+#include <linux/module.h>

#include "idmap.h"
#include "cache.h"
@@ -511,11 +512,22 @@ static __be32
nfsd4_putfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_putfh *putfh)
{
+ __be32 ret;
+
fh_put(&cstate->current_fh);
cstate->current_fh.fh_handle.fh_size = putfh->pf_fhlen;
memcpy(&cstate->current_fh.fh_handle.fh_base, putfh->pf_fhval,
putfh->pf_fhlen);
- return fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_BYPASS_GSS);
+ ret = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_BYPASS_GSS);
+ if (ret == nfserr_stale && HAS_CSTATE_FLAG(cstate, NO_VERIFY_FH)) {
+ printk("%s SET IS_STALE_FH\n", __func__);
+ CLEAR_CSTATE_FLAG(cstate, NO_VERIFY_FH);
+ SET_CSTATE_FLAG(cstate, IS_STALE_FH);
+ ret = 0;
+ }
+ dprintk("<-- %s cstate %p, cstate->sid_flags 0x%x\n", __func__, cstate,
+ cstate->sid_flags);
+ return ret;
}

static __be32
@@ -548,6 +560,19 @@ static __be32
nfsd4_savefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
void *arg)
{
+ if (HAS_CSTATE_FLAG(cstate, IS_STALE_FH)) {
+ dprintk("%s IS_STALE_FH\n", __func__);
+ /**
+ * This is either an inter COPY (most likely) or an
+ * intra COPY with a stale file handle.
+ * If the latter, nfsd4_copy will reset the PUTFH to return
+ * nfserr_stale.
+ * No fh_dentry, just copy the file handle
+ * to use with the inter COPY READ.
+ */
+ cstate->save_fh = cstate->current_fh;
+ return nfs_ok;
+ }
if (!cstate->current_fh.fh_dentry)
return nfserr_nofilehandle;

@@ -1057,6 +1082,13 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status)
return status;

+ /* Intra copy source fh is stale */
+ if (HAS_CSTATE_FLAG(cstate, IS_STALE_FH)) {
+ CLEAR_CSTATE_FLAG(cstate, IS_STALE_FH);
+ cstate->status = nfserr_copy_stalefh;
+ goto out;
+ }
+
bytes = nfsd_copy_range(src, copy->cp_src_pos,
dst, copy->cp_dst_pos,
copy->cp_count);
@@ -1074,6 +1106,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,

fput(src);
fput(dst);
+out:
return status;
}

@@ -1759,6 +1792,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate = &resp->cstate;
struct svc_fh *current_fh = &cstate->current_fh;
struct svc_fh *save_fh = &cstate->save_fh;
+ int i;
__be32 status;

svcxdr_init_encode(rqstp, resp);
@@ -1791,6 +1825,12 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
goto encode_op;
}

+ /* NFSv4.2 COPY source file handle may be from a different server */
+ for (i = 0; i < args->opcnt; i++) {
+ op = &args->ops[i];
+ if (op->opnum == OP_COPY)
+ SET_CSTATE_FLAG(cstate, NO_VERIFY_FH);
+ }
while (!status && resp->opcnt < args->opcnt) {
op = &args->ops[resp->opcnt++];

@@ -1810,6 +1850,11 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,

opdesc = OPDESC(op);

+ if (HAS_CSTATE_FLAG(cstate, IS_STALE_FH)) {
+ printk("%s IS_STALE_FH\n", __func__);
+ goto call_op;
+ }
+
if (!current_fh->fh_dentry) {
if (!(opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
op->status = nfserr_nofilehandle;
@@ -1844,6 +1889,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,

if (opdesc->op_get_currentstateid)
opdesc->op_get_currentstateid(cstate, &op->u);
+call_op:
op->status = opdesc->op_func(rqstp, cstate, &op->u);

if (!op->status) {
@@ -1864,6 +1910,14 @@ encode_op:
status = op->status;
goto out;
}
+ /* Only from intra COPY */
+ if (cstate->status == nfserr_copy_stalefh) {
+ dprintk("%s NFS4.2 intra COPY stale src filehandle\n",
+ __func__);
+ status = nfserr_stale;
+ nfsd4_adjust_encode(resp);
+ goto out;
+ }
if (op->status == nfserr_replay_me) {
op->replay = &cstate->replay_owner->so_replay;
nfsd4_encode_replay(&resp->xdr, op);
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index afcb6c4..77ae4e5 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -4576,15 +4576,28 @@ __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize)
return nfserr_rep_too_big;
}

+/** Rewind the encoding to return nfserr_stale on the PUTFH
+ * in this failed Intra COPY compound
+ */
+void
+nfsd4_adjust_encode(struct nfsd4_compoundres *resp)
+{
+ __be32 *p;
+
+ p = resp->cstate.putfh_errp;
+ *p++ = nfserr_stale;
+}
+
void
nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
{
struct xdr_stream *xdr = &resp->xdr;
struct nfs4_stateowner *so = resp->cstate.replay_owner;
+ struct nfsd4_compound_state *cstate = &resp->cstate;
struct svc_rqst *rqstp = resp->rqstp;
int post_err_offset;
nfsd4_enc encoder;
- __be32 *p;
+ __be32 *p, *statp;

p = xdr_reserve_space(xdr, 8);
if (!p) {
@@ -4593,9 +4606,20 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
}
*p++ = cpu_to_be32(op->opnum);
post_err_offset = xdr->buf->len;
+ statp = p;

if (op->opnum == OP_ILLEGAL)
goto status;
+
+ /** This is a COPY compound with a stale source server file handle.
+ * If OP_COPY processing determines that this is an intra server to
+ * server COPY, then this PUTFH should return nfserr_ stale so the
+ * putfh_errp will be set to nfserr_stale. If this is an inter server
+ * to server COPY, ignore the nfserr_stale.
+ */
+ if (op->opnum == OP_PUTFH && HAS_CSTATE_FLAG(cstate, IS_STALE_FH))
+ cstate->putfh_errp = statp;
+
BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
!nfsd4_enc_ops[op->opnum]);
encoder = nfsd4_enc_ops[op->opnum];
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 565c4da..f676373 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -267,6 +267,8 @@ void nfsd_lockd_shutdown(void);
#define nfserr_replay_me cpu_to_be32(11001)
/* nfs41 replay detected */
#define nfserr_replay_cache cpu_to_be32(11002)
+/* nfs42 intra copy failed with nfserr_stale */
+#define nfserr_copy_stalefh cpu_to_be32(1103)

/* Check for dir entries '.' and '..' */
#define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 4d6f2fe7..7e33570 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -46,6 +46,8 @@

#define CURRENT_STATE_ID_FLAG (1<<0)
#define SAVED_STATE_ID_FLAG (1<<1)
+#define NO_VERIFY_FH (1<<2)
+#define IS_STALE_FH (1<<3)

#define SET_CSTATE_FLAG(c, f) ((c)->sid_flags |= (f))
#define HAS_CSTATE_FLAG(c, f) ((c)->sid_flags & (f))
@@ -63,6 +65,7 @@ struct nfsd4_compound_state {
size_t iovlen;
u32 minorversion;
__be32 status;
+ __be32 *putfh_errp;
stateid_t current_stateid;
stateid_t save_stateid;
/* to indicate current and saved state id presents */
@@ -702,6 +705,7 @@ int nfs4svc_decode_compoundargs(struct svc_rqst *, __be32 *,
int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *,
struct nfsd4_compoundres *);
__be32 nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);
+void nfsd4_adjust_encode(struct nfsd4_compoundres *);
void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *);
void nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op);
__be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
--
1.8.3.1


2015-03-17 22:41:42

by Andy Adamson

[permalink] [raw]
Subject: [PATCH RFC 06/10] NFS add ca_source_server<> to COPY

From: Andy Adamson <[email protected]>

Support only one source server address: the same address that the client and
source server use.

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfs/nfs42.h | 3 ++-
fs/nfs/nfs42proc.c | 18 +++++++++++++++++-
fs/nfs/nfs42xdr.c | 30 +++++++++++++++++++++++++-----
fs/nfs/nfs4file.c | 6 ++++--
include/linux/nfs_xdr.h | 13 +++++++++++++
5 files changed, 61 insertions(+), 9 deletions(-)

diff --git a/fs/nfs/nfs42.h b/fs/nfs/nfs42.h
index 849b51e..e5a0b17 100644
--- a/fs/nfs/nfs42.h
+++ b/fs/nfs/nfs42.h
@@ -7,7 +7,8 @@

/* nfs4.2proc.c */
int nfs42_proc_allocate(struct file *, loff_t, loff_t);
-ssize_t nfs42_proc_copy(struct file *, loff_t, struct file *, loff_t, size_t);
+ssize_t nfs42_proc_copy(struct file *, loff_t, struct file *, loff_t,
+ size_t, struct nfs42_copy_notify_res *);
int nfs42_proc_copy_notify(struct file *, struct file *,
struct nfs42_copy_notify_res *);
int nfs42_proc_deallocate(struct file *, loff_t, loff_t);
diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index e09e793..325adb7 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -146,9 +146,12 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
return err;
}

+/**
+ * Support one source address for now.
+ */
ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
struct file *dst, loff_t pos_dst,
- size_t count)
+ size_t count, struct nfs42_copy_notify_res *cn_res)
{
struct nfs42_copy_args args = {
.src_fh = NFS_FH(file_inode(src)),
@@ -166,6 +169,19 @@ ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
struct nfs_server *server = NFS_SERVER(file_inode(dst));
int status;

+ if (cn_res) {
+ args.cp_nsrc = 1; /* support for one NL4_NETADDR for now */
+ args.src[0].cp_nl_type = cn_res->src[0].cnr_nl_type;
+ args.src[0].u.cp_addr = cn_res->src[0].u.cnr_addr;
+
+ dprintk("--> %s nl4_type %dcp_addr netid %d:%s uaddr %d:%s\n",
+ __func__, args.src[0].cp_nl_type,
+ args.src[0].u.cp_addr.na_netid_len,
+ args.src[0].u.cp_addr.na_netid,
+ args.src[0].u.cp_addr.na_uaddr_len,
+ args.src[0].u.cp_addr.na_uaddr);
+ }
+
if (!(server->caps & NFS_CAP_COPY))
return -ENOTSUPP;

diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c
index 94a484a..38d9a6a 100644
--- a/fs/nfs/nfs42xdr.c
+++ b/fs/nfs/nfs42xdr.c
@@ -1,6 +1,3 @@
-/*
- * Copyright (c) 2014 Anna Schumaker <[email protected]>
- */
#ifndef __LINUX_FS_NFS_NFS4_2XDR_H
#define __LINUX_FS_NFS_NFS4_2XDR_H

@@ -18,7 +15,10 @@
#define encode_copy_maxsz (op_encode_hdr_maxsz + \
XDR_QUADLEN(NFS4_STATEID_SIZE) + \
XDR_QUADLEN(NFS4_STATEID_SIZE) + \
- 2 + 2 + 2 + 1 + 1 + 1)
+ 2 + 2 + 2 + 1 + 1 + 1 +\
+ 1 + /* One cnr_source_server */\
+ 1 + /* nl4_type */ \
+ 1 + XDR_QUADLEN(NFS4_OPAQUE_LIMIT))
#define decode_copy_maxsz (op_decode_hdr_maxsz + \
NFS42_WRITE_RES_SIZE + \
1 /* cr_consecutive */ + \
@@ -131,7 +131,27 @@ static void encode_copy(struct xdr_stream *xdr,

encode_uint32(xdr, 1); /* consecutive = true */
encode_uint32(xdr, 1); /* synchronous = true */
- encode_uint32(xdr, 0); /* src server list */
+ encode_uint32(xdr, args->cp_nsrc); /* set to 1 for inter-ssc*/
+ if (args->cp_nsrc ==0) /* intra-ssc */
+ return;
+
+ encode_uint32(xdr, args->src[0].cp_nl_type);
+ /* Support one src server in list for now */
+ switch(args->src[0].cp_nl_type) {
+ case NL4_NAME:
+ case NL4_URL:
+ encode_string(xdr, args->src[0].u.cp_str_sz,
+ args->src[0].u.cp_str);
+ break;
+ case NL4_NETADDR:
+ encode_string(xdr, args->src[0].u.cp_addr.na_netid_len,
+ args->src[0].u.cp_addr.na_netid);
+ encode_string(xdr, args->src[0].u.cp_addr.na_uaddr_len,
+ args->src[0].u.cp_addr.na_uaddr);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
}

static void encode_copy_notify(struct xdr_stream *xdr,
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index c71bf6a..a72f402 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -140,7 +140,8 @@ static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb ||
file_in->f_path.mnt != file_out->f_path.mnt)
return -ENOTSUPP;
- return nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count);
+ return nfs42_proc_copy(file_in, pos_in, file_out, pos_out,
+ count, NULL);
} else { /* Inter-ssc */
struct nfs42_copy_notify_res cn_res = {
.cnr_nsrc = 0,
@@ -150,7 +151,8 @@ static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
err = nfs42_proc_copy_notify(file_in, file_out, &cn_res);
if (err)
return err;
- return nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count);
+ return nfs42_proc_copy(file_in, pos_in, file_out, pos_out,
+ count, &cn_res);
}
}

diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 984e0e6..cf1accc 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1300,6 +1300,19 @@ struct nfs42_copy_args {
u64 dst_pos;

u64 count;
+ /* Support one source server */
+ int cp_nsrc;
+ struct { /* ca_source_server<> */
+ enum netloc_type4 cp_nl_type;
+ union {
+ struct {
+ /* NL4_NAME, NL4_URL */
+ int cp_str_sz;
+ char cp_str[NFS4_OPAQUE_LIMIT + 1];
+ };
+ struct nfs42_netaddr cp_addr; /* NL4_NETADDR */
+ } u;
+ } src[NFS42_MAX_SSC_SRC];
};

struct nfs42_write_res {
--
1.8.3.1


2015-03-18 18:51:41

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH RFC 00/10] NFSv4.2 Inter server to server copy RFC

On Tue, Mar 17, 2015 at 06:31:28PM -0400, [email protected] wrote:
> From: Andy Adamson <[email protected]>
>
> Disclaimer: this code is in early development. Please excuse printk's etc.
>
> This RFC is for us to determine if there are objections, suggestions, or comments on our approach. This code builds on top of Anna's intra-server copy patch set which uses the new vfs_copy_file_range call. This code can perform an inter-ssc copy using NFSv4.1 as the copy protocol; the destination server loads the nfs42-interserver-copy module, mounts the source server, sets up an NFS filep to pass to vfs_copy_file_range which READs the file to be copied over NFSv4.1. The destination server then cleans up. Multiple copies do not currently work.
>
> Objectives:
> -----------
>
> - Use NFSv4.1 for the copy protocol betweent the source and destination servers to READ the data to be copied.
> - Call the new vfs_copy_file_range function.
> - Do not call NFSv4.1 OPEN from the destination server. Instead, use the COPY ca_src_stateid and the COPY SAVE_FH filehandle, which are the file handle and stateid from the OPEN on the client by the user.
>
> Some questions
> --------------
> 1) NFS interserver copy module
>
> The basic design is for the destination server to load a new nfs42-interserver-copy NFS kernel module to setup the struct filp for vfs_copy_file_range to use.
> - We prefer the 'load NFS module' design to other designs such as an upcall to a userland daemon. Comments?

I'm fine with doing it in the kernel. I don't think it's especially
important that it be its own module. You do need an upcall for the name
resolution, though--the referral code does this already, doesn't it? In
which case, copy whatever it does....

Or maybe consider dropping those cases from the protocol--why do we need
the NL4_NAME case? And NL4_URL is even more cases to handle. In
practice the implementations are all going to settle on one protocol if
they want to interoperate anyway.

If we really want to be able to handle http, ftp, or any other random
protocol, then that would be an argument for doing more in userspace,
but it would be simpler just to pick one.

--b.

> 2) NFS module connecting to the source server.
>
> The module calls nfs_fs_mount using the NL4_NETADDR address in string form concatenated with ":/".
> We need to end up with a struct file that represents an NFSv4.1 open file to use for the READ. The alloc_file function requires a vfsmount struct in the struct path argument.
> Patch "NFS return the root_mnt via the raw data in nfs_fs_mount" makes the vfsmount struct from nfs_fs_mount available.
>
> Comments on this approach?
>
> Thanks
>
> Andy Adamson
>
>
> Andy Adamson (10):
> NFS return the root_mnt via the raw data in nfs_fs_mount
> NFS interserver ssc module
> NFS add COPY_NOTIFY operation
> NFSD add ca_source_server<> to COPY
> NFSD add COPY_NOTIFY operation
> NFS add ca_source_server<> to COPY
> NFSD generalize nfsd4_compound_state flag names
> NFSD: allow inter server COPY to have a STALE source server fh
> NFSD: add nfs4interssc.c
> NFSD nfs4 inter ssc copy
>
> fs/nfs/Kconfig | 10 +
> fs/nfs/Makefile | 3 +
> fs/nfs/internal.h | 16 ++
> fs/nfs/nfs42.h | 5 +-
> fs/nfs/nfs42proc.c | 77 +++++++-
> fs/nfs/nfs42xdr.c | 196 ++++++++++++++++++-
> fs/nfs/nfs4_fs.h | 1 +
> fs/nfs/nfs4client.c | 30 +++
> fs/nfs/nfs4file.c | 32 ++-
> fs/nfs/nfs4intercopy.c | 419 ++++++++++++++++++++++++++++++++++++++++
> fs/nfs/nfs4proc.c | 8 +-
> fs/nfs/nfs4state.c | 3 +
> fs/nfs/nfs4super.c | 2 +
> fs/nfs/nfs4xdr.c | 1 +
> fs/nfs/super.c | 27 +++
> fs/nfsd/Makefile | 2 +-
> fs/nfsd/nfs4interssc.c | 110 +++++++++++
> fs/nfsd/nfs4proc.c | 307 ++++++++++++++++++++++++++++-
> fs/nfsd/nfs4state.c | 6 +-
> fs/nfsd/nfs4xdr.c | 180 ++++++++++++++++-
> fs/nfsd/nfsd.h | 2 +
> fs/nfsd/xdr4.h | 40 +++-
> include/linux/nfs4.h | 7 +
> include/linux/nfs4intercopy.h | 44 +++++
> include/linux/nfs_fs_sb.h | 1 +
> include/linux/nfs_xdr.h | 54 ++++++
> include/uapi/linux/nfs4_mount.h | 1 +
> 27 files changed, 1548 insertions(+), 36 deletions(-)
> create mode 100644 fs/nfs/nfs4intercopy.c
> create mode 100644 fs/nfsd/nfs4interssc.c
> create mode 100644 include/linux/nfs4intercopy.h
>
> --
> 1.8.3.1