2017-09-28 17:30:17

by Olga Kornievskaia

[permalink] [raw]
Subject: [PATCH v4 0/8] NFSD support for "inter" server-to-server COPY

This depends on the async NFSD series and it depends on the client's
inter copy series that provide nfs4_ssc_open/close functions and
reboot recovery support for the destination server.

NFSD determines if COPY is intra or inter and if sync or async. For
inter, NSFD uses NFSv4.1 protocol and creates an internal mount point
(superblock). It will destroy the mount point when copy is done.

On the source server, upon receiving a COPY_NOTIFY, it generate a
unique stateid that's kept in the global list. Upon receiving a READ
with a stateid, the code checks the normal list of open stateid and
now additionally, it'll check the copy state list as well before
deciding to either fail with BAD_STATEID or find one that matches.
The stored stateid is only valid to be used for the first time
with a choosen lease period (90s currently). When the source server
received an OFFLOAD_CANCEL, it will remove the stateid from the
global list. Otherwise, the copy stateid is removed upon the removal
of its "parent" stateid (open/lock/delegation stateid).

v4:
* Don't have the IS_STALE_FH in the compount state structure. Instead
mark the filehandle FOREIGN. If the server gets a PUTFH,SAVEFH,PUTFH...
COPY compount then it'll allow for the PUTFH to be STALE (foreign)
handle. It
* This series has Bruce's proposed "nfsd: remove unnecessary nofilehandle
checks" patch as I didn't see it committed yet.
* dropped the support for multiple server netaddrs in the COPY_NOTIFY/
COPY
* made sure to return OFFLOAD_DENIED when mount to the source server
fails which triggers the client to fallback on the traditional copy.

Andy Adamson (1):
NFSD generalize nfsd4_compound_state flag names

Olga Kornievskaia (7):
NFSD fill-in netloc4 structure
NFSD add ca_source_server<> to COPY
NFSD add COPY_NOTIFY operation
NFSD check stateids against copy stateids
nfsd: remove unnecessary nofilehandle checks
NFSD: allow inter server COPY to have a STALE source server fh
NFSD add nfs4 inter ssc to nfsd4_copy

fs/nfsd/Kconfig | 10 ++
fs/nfsd/nfs4proc.c | 427 ++++++++++++++++++++++++++++++++++++++++++++++-----
fs/nfsd/nfs4state.c | 37 ++++-
fs/nfsd/nfs4xdr.c | 168 +++++++++++++++++++-
fs/nfsd/nfsd.h | 31 +++-
fs/nfsd/nfsfh.h | 5 +-
fs/nfsd/state.h | 5 +-
fs/nfsd/xdr4.h | 25 ++-
include/linux/nfs4.h | 1 +
9 files changed, 657 insertions(+), 52 deletions(-)

--
1.8.3.1



2017-09-28 17:30:04

by Olga Kornievskaia

[permalink] [raw]
Subject: [PATCH v4 1/8] NFSD fill-in netloc4 structure

nfs.4 defines nfs42_netaddr structure that represents netloc4.

Populate needed fields from the sockaddr structure.

This will be used by flexfiles and 4.2 inter copy

Signed-off-by: Olga Kornievskaia <[email protected]>
---
fs/nfsd/nfsd.h | 31 ++++++++++++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)

diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index b9c538a..2fc3030 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -17,7 +17,7 @@
#include <linux/nfs4.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/msg_prot.h>
-
+#include <linux/sunrpc/addr.h>
#include <uapi/linux/nfsd/debug.h>

#include "stats.h"
@@ -364,6 +364,35 @@ static inline bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)

extern const u32 nfsd_suppattrs[3][3];

+static inline u32 nfsd4_set_netaddr(struct sockaddr *addr, struct nfs42_netaddr *netaddr)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
+ unsigned int port;
+ size_t ret;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ port = ntohs(sin->sin_port);
+ sprintf(netaddr->netid, "tcp");
+ netaddr->netid_len = 3;
+ break;
+ case AF_INET6:
+ port = ntohs(sin6->sin6_port);
+ sprintf(netaddr->netid, "tcp6");
+ netaddr->netid_len = 4;
+ break;
+ default:
+ return nfserrno(-EINVAL);
+ }
+ ret = rpc_ntop(addr, netaddr->addr, sizeof(netaddr->addr));
+ netaddr->addr_len = ret + snprintf(netaddr->addr + ret,
+ RPCBIND_MAXUADDRLEN + 1,
+ ".%u.%u", port >> 8, port & 0xff);
+
+ return 0;
+}
+
static inline bool bmval_is_subset(const u32 *bm1, const u32 *bm2)
{
return !((bm1[0] & ~bm2[0]) ||
--
1.8.3.1


2017-09-28 17:30:06

by Olga Kornievskaia

[permalink] [raw]
Subject: [PATCH v4 4/8] NFSD check stateids against copy stateids

Incoming stateid (used by a READ) could be a saved copy stateid.
On first use make it active and check that the copy has started
within the allowable lease time.

Signed-off-by: Olga Kornievskaia <[email protected]>
---
fs/nfsd/nfs4state.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 9932d50..e28447d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -770,6 +770,31 @@ __be32 find_cp_state(struct nfsd_net *nn, stateid_t *st,
*cps = state;
return 0;
}
+/*
+ * A READ from an inter server to server COPY will have a
+ * copy stateid. Return the parent nfs4_stid.
+ */
+static __be32 find_cp_state_parent(struct nfsd_net *nn, stateid_t *st,
+ struct nfs4_stid **stid)
+{
+ __be32 status;
+ struct nfs4_cp_state *cps = NULL;
+
+ status = find_cp_state(nn, st, &cps);
+ if (status)
+ return status;
+
+ /* Did the inter server to server copy start in time? */
+ if (cps->cp_active == false && !time_after(cps->cp_timeout, jiffies))
+ return nfserr_partner_no_auth;
+ else
+ cps->cp_active = true;
+
+ *stid = cps->cp_p_stid;
+ atomic_inc(&cps->cp_p_stid->sc_count);
+
+ return nfs_ok;
+}

/*
* When we recall a delegation, we should be careful not to hand it
@@ -5054,6 +5079,8 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
status = nfsd4_lookup_stateid(cstate, stateid,
NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
&s, nn);
+ if (status == nfserr_bad_stateid)
+ status = find_cp_state_parent(nn, stateid, &s);
if (status)
return status;
status = check_stateid_generation(stateid, &s->sc_stateid,
--
1.8.3.1


2017-09-28 17:30:10

by Olga Kornievskaia

[permalink] [raw]
Subject: [PATCH v4 8/8] NFSD add nfs4 inter ssc to nfsd4_copy

Given a universal address, mount the source server from the destination
server. Use an internal mount. Call the NFS client nfs42_ssc_open to
obtain the NFS struct file suitable for nfsd_copy_range.

Signed-off-by: Andy Adamson <[email protected]>
Signed-off-by: Olga Kornievskaia <[email protected]>
---
fs/nfsd/nfs4proc.c | 297 +++++++++++++++++++++++++++++++++++++++++++++++----
fs/nfsd/xdr4.h | 4 +
include/linux/nfs4.h | 1 +
3 files changed, 283 insertions(+), 19 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 18d1de4..8389639 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1110,6 +1110,230 @@ static int fill_in_write_vector(struct kvec *vec, struct nfsd4_write *write)
out:
return status;
}
+
+#ifdef CONFIG_NFSD_V4_2_INTER_SSC
+
+extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt,
+ struct nfs_fh *src_fh,
+ nfs4_stateid *stateid);
+extern void nfs42_ssc_close(struct file *filep);
+
+extern void nfs_sb_deactive(struct super_block *sb);
+
+#define NFSD42_INTERSSC_MOUNTOPS "minorversion=2,vers=4,addr=%s,clientaddr=%s"
+
+/**
+ * Support one copy source server for now.
+ */
+static struct vfsmount *
+nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp)
+{
+ struct file_system_type *type;
+ struct vfsmount *ss_mnt;
+ struct nfs42_netaddr *naddr;
+ struct sockaddr_storage tmp_addr;
+ size_t tmp_addrlen, match_netid_len = 3;
+ char *startsep = "", *endsep = "", *match_netid = "tcp";
+ char *ipaddr, *ipaddr2, *raw_data;
+ int len, raw_len, status = -EINVAL;
+
+ /* Currently support only NL4_NETADDR source server */
+ if (nss->nl4_type != NL4_NETADDR) {
+ WARN(nss->nl4_type != NL4_NETADDR,
+ "nfsd4_copy src server not NL4_NETADDR\n");
+ goto out_err;
+ }
+
+ naddr = &nss->u.nl4_addr;
+
+ tmp_addrlen = rpc_uaddr2sockaddr(SVC_NET(rqstp), naddr->addr,
+ naddr->addr_len,
+ (struct sockaddr *)&tmp_addr,
+ sizeof(tmp_addr));
+ if (tmp_addrlen == 0)
+ goto out_err;
+
+ if (tmp_addr.ss_family == AF_INET6) {
+ startsep = "[";
+ endsep = "]";
+ match_netid = "tcp6";
+ match_netid_len = 4;
+ }
+
+ if (naddr->netid_len != match_netid_len ||
+ strncmp(naddr->netid, match_netid, naddr->netid_len))
+ goto out_err;
+
+ /* Construct the raw data for the vfs_kern_mount call */
+ len = RPC_MAX_ADDRBUFLEN + 1;
+ ipaddr = kzalloc(len, GFP_KERNEL);
+ if (!ipaddr)
+ goto out_err;
+
+ rpc_ntop((struct sockaddr *)&tmp_addr, ipaddr, len);
+
+ /* 2 for ipv6 endsep and startsep. 3 for ":/" and trailing '/0'*/
+ ipaddr2 = kzalloc(len + 5, GFP_KERNEL);
+ if (!ipaddr2)
+ goto out_free_ipaddr;
+
+ rpc_ntop((struct sockaddr *)&rqstp->rq_daddr, ipaddr2, len + 5);
+
+ raw_len = strlen(NFSD42_INTERSSC_MOUNTOPS) + strlen(ipaddr) +
+ strlen(ipaddr2);
+ raw_data = kzalloc(raw_len, GFP_KERNEL);
+ if (!raw_data)
+ goto out_free_ipaddr2;
+
+ snprintf(raw_data, raw_len, NFSD42_INTERSSC_MOUNTOPS, ipaddr,
+ ipaddr2);
+
+ status = -ENODEV;
+ type = get_fs_type("nfs");
+ if (!type)
+ goto out_free_rawdata;
+
+ /* Set the server:<export> for the vfs_kerne_mount call */
+ memset(ipaddr2, 0, len + 5);
+ snprintf(ipaddr2, len + 5, "%s%s%s:/", startsep, ipaddr, endsep);
+
+ dprintk("%s Raw mount data: %s server:export %s\n", __func__,
+ raw_data, ipaddr2);
+
+ /* Use an 'internal' mount: MS_KERNMOUNT -> MNT_INTERNAL */
+ ss_mnt = vfs_kern_mount(type, MS_KERNMOUNT, ipaddr2, raw_data);
+ if (IS_ERR(ss_mnt)) {
+ status = PTR_ERR(ss_mnt);
+ goto out_free_rawdata;
+ }
+
+ kfree(raw_data);
+ kfree(ipaddr2);
+ kfree(ipaddr);
+
+ return ss_mnt;
+
+out_free_rawdata:
+ kfree(raw_data);
+out_free_ipaddr2:
+ kfree(ipaddr2);
+out_free_ipaddr:
+ kfree(ipaddr);
+out_err:
+ dprintk("--> %s ERROR %d\n", __func__, status);
+ return ERR_PTR(status);
+}
+
+static void
+nfsd4_interssc_disconnect(struct vfsmount *ss_mnt)
+{
+ nfs_sb_deactive(ss_mnt->mnt_sb);
+ mntput(ss_mnt);
+}
+
+/**
+ * nfsd4_setup_inter_ssc
+ *
+ * Verify COPY destination stateid.
+ * Connect to the source server with NFSv4.1.
+ * Create the source struct file for nfsd_copy_range.
+ * Called with COPY cstate:
+ * SAVED_FH: source filehandle
+ * CURRENT_FH: destination filehandle
+ *
+ * Returns errno (not nfserrxxx)
+ */
+static struct vfsmount *
+nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
+ struct nfsd4_compound_state *cstate,
+ struct nfsd4_copy *copy)
+{
+ struct svc_fh *s_fh = NULL;
+ stateid_t *s_stid = &copy->cp_src_stateid;
+ struct vfsmount *ss_mnt;
+ __be32 status;
+
+ /* Verify the destination stateid and set dst struct file*/
+ status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
+ &copy->cp_dst_stateid,
+ WR_STATE, &copy->fh_dst, NULL,
+ &copy->stid);
+ if (status) {
+ ss_mnt = ERR_PTR(be32_to_cpu(status));
+ goto out;
+ }
+
+ ss_mnt = nfsd4_interssc_connect(copy->cp_src, rqstp);
+ if (IS_ERR(ss_mnt))
+ goto out;
+
+ s_fh = &cstate->save_fh;
+
+ copy->c_fh.size = s_fh->fh_handle.fh_size;
+ memcpy(copy->c_fh.data, &s_fh->fh_handle.fh_base, copy->c_fh.size);
+ copy->stateid.seqid = s_stid->si_generation;
+ memcpy(copy->stateid.other, (void *)&s_stid->si_opaque,
+ sizeof(stateid_opaque_t));
+
+out:
+ return ss_mnt;
+}
+
+static void
+nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct file *src,
+ struct file *dst)
+{
+ nfs42_ssc_close(src);
+ fput(src);
+ fput(dst);
+}
+
+#else /* CONFIG_NFSD_V4_2_INTER_SSC */
+
+static struct vfsmount *
+nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
+ struct nfsd4_compound_state *cstate,
+ struct nfsd4_copy *copy)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static void
+nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct file *src,
+ struct file *dst)
+{
+}
+
+static void
+nfsd4_interssc_disconnect(struct vfsmount *ss_mnt)
+{
+}
+
+static struct file *nfs42_ssc_open(struct vfsmount *ss_mnt,
+ struct nfs_fh *src_fh,
+ nfs4_stateid *stateid)
+{
+ return NULL;
+}
+#endif /* CONFIG_NFSD_V4_2_INTER_SSC */
+
+static __be32
+nfsd4_setup_intra_ssc(struct svc_rqst *rqstp,
+ struct nfsd4_compound_state *cstate,
+ struct nfsd4_copy *copy)
+{
+ return nfsd4_verify_copy(rqstp, cstate, &copy->cp_src_stateid,
+ &copy->fh_src, &copy->cp_dst_stateid,
+ &copy->fh_dst, &copy->stid);
+}
+
+static void
+nfsd4_cleanup_intra_ssc(struct file *src, struct file *dst)
+{
+ fput(src);
+ fput(dst);
+}
+
static void nfsd4_cb_offload_release(struct nfsd4_callback *cb)
{
struct nfsd4_copy *copy = container_of(cb, struct nfsd4_copy, cp_cb);
@@ -1179,12 +1403,16 @@ static int nfsd4_do_copy(struct nfsd4_copy *copy, bool sync)
else
status = nfsd4_init_copy_res(copy, sync);

- fput(copy->fh_src);
- fput(copy->fh_dst);
+ if (copy->cp_src) /* Inter server SSC */
+ nfsd4_cleanup_inter_ssc(copy->ss_mnt, copy->fh_src,
+ copy->fh_dst);
+ else
+ nfsd4_cleanup_intra_ssc(copy->fh_src, copy->fh_dst);
+
return status;
}

-static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst)
+static int dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst)
{
memcpy(&dst->cp_src_stateid, &src->cp_src_stateid, sizeof(stateid_t));
memcpy(&dst->cp_dst_stateid, &src->cp_dst_stateid, sizeof(stateid_t));
@@ -1203,6 +1431,17 @@ static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst)
dst->net = src->net;
dst->stid = src->stid;
dst->cps = src->cps;
+ if (src->cp_src) {
+ dst->cp_src = kmalloc(sizeof(struct nl4_server), GFP_KERNEL);
+ if (!dst->cp_src)
+ return -ENOMEM;
+ memcpy(dst->cp_src, src->cp_src, sizeof(struct nl4_server));
+ }
+ memcpy(&dst->stateid, &src->stateid, sizeof(src->stateid));
+ memcpy(&dst->c_fh, &src->c_fh, sizeof(src->c_fh));
+ dst->ss_mnt = src->ss_mnt;
+
+ return 0;
}

static void nfsd4_do_async_copy(struct work_struct *work)
@@ -1214,11 +1453,22 @@ static void nfsd4_do_async_copy(struct work_struct *work)
if (test_bit(NFSD4_CLIENT_COPY_KILL, &copy->cp_clp->cl_flags))
goto out;

+ if (copy->cp_src) { /* Inter server SSC */
+ copy->fh_src = nfs42_ssc_open(copy->ss_mnt, &copy->c_fh,
+ &copy->stateid);
+ if (IS_ERR(copy->fh_src)) {
+ copy->nfserr = nfserr_offload_denied;
+ nfsd4_interssc_disconnect(copy->ss_mnt);
+ goto do_callback;
+ }
+ }
+
copy->nfserr = nfsd4_do_copy(copy, 0);

if (copy->cps->cp_cancelled)
goto out;

+do_callback:
cb_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL);
if (!cb_copy)
goto out;
@@ -1233,6 +1483,7 @@ static void nfsd4_do_async_copy(struct work_struct *work)
list_del(&copy->cps->cp_list);
nfs4_free_cp_state(copy->cps);
nfs4_put_stid(copy->stid);
+ kfree(copy->cp_src);
kfree(copy);
}

@@ -1242,12 +1493,19 @@ static void nfsd4_do_async_copy(struct work_struct *work)
{
struct nfsd4_copy *copy = &u->copy;
__be32 status;
+ struct nfsd4_copy *async_copy;

- status = nfsd4_verify_copy(rqstp, cstate, &copy->cp_src_stateid,
- &copy->fh_src, &copy->cp_dst_stateid,
- &copy->fh_dst, &copy->stid);
- if (status)
- goto out;
+ if (copy->cp_src) { /* Inter server SSC */
+ copy->ss_mnt = nfsd4_setup_inter_ssc(rqstp, cstate, copy);
+ if (IS_ERR(copy->ss_mnt)) {
+ status = nfserr_offload_denied;
+ goto out;
+ }
+ } else {
+ status = nfsd4_setup_intra_ssc(rqstp, cstate, copy);
+ if (status)
+ goto out;
+ }

copy->cp_clp = cstate->clp;
memcpy(&copy->fh, &cstate->current_fh.fh_handle,
@@ -1255,28 +1513,24 @@ static void nfsd4_do_async_copy(struct work_struct *work)
copy->net = SVC_NET(rqstp);
if (!copy->cp_synchronous) {
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
- struct nfsd4_copy *async_copy;

status = nfsd4_init_copy_res(copy, 0);
async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL);
- if (!async_copy) {
- status = nfserrno(-ENOMEM);
- goto out;
- }
+ if (!async_copy)
+ goto out_err;
copy->cps = nfs4_alloc_init_cp_state(nn, nn->nfsd4_lease,
copy->stid);
- if (!copy->cps) {
- status = nfserrno(-ENOMEM);
- kfree(async_copy);
- goto out;
- }
+ if (!copy->cps)
+ goto out_err;
/* take a reference on the parent stateid so it's not
* not freed by the copy compound
*/
atomic_inc(&copy->stid->sc_count);
memcpy(&copy->cp_res.cb_stateid, &copy->cps->cp_stateid,
sizeof(copy->cps->cp_stateid));
- dup_copy_fields(copy, async_copy);
+ status = dup_copy_fields(copy, async_copy);
+ if (status)
+ goto out_err;
INIT_WORK(&async_copy->cp_work, nfsd4_do_async_copy);
queue_work(copy_wq, &async_copy->cp_work);
} else {
@@ -1284,6 +1538,11 @@ static void nfsd4_do_async_copy(struct work_struct *work)
}
out:
return status;
+out_err:
+ kfree(async_copy);
+ status = nfserrno(-ENOMEM);
+ nfsd4_interssc_disconnect(copy->ss_mnt);
+ goto out;
}

static __be32
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 82d714b..e00ed6b 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -540,6 +540,10 @@ struct nfsd4_copy {
struct net *net;
struct nfs4_stid *stid;
struct nfs4_cp_state *cps;
+
+ struct vfsmount *ss_mnt;
+ struct nfs_fh c_fh;
+ nfs4_stateid stateid;
};

struct nfsd4_seek {
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index aff3121..627bf9f 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -16,6 +16,7 @@
#include <linux/uidgid.h>
#include <uapi/linux/nfs4.h>
#include <linux/sunrpc/msg_prot.h>
+#include <linux/nfs.h>

enum nfs4_acl_whotype {
NFS4_ACL_WHO_NAMED = 0,
--
1.8.3.1


2017-09-28 17:30:10

by Olga Kornievskaia

[permalink] [raw]
Subject: [PATCH v4 2/8] NFSD add ca_source_server<> to COPY

Note: followed conventions and have struct nfsd4_compoundargs pointer as a
parameter even though it is unused.

Signed-off-by: Andy Adamson <[email protected]>
Signed-off-by: Olga Kornievskaia <[email protected]>
---
fs/nfsd/nfs4xdr.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
fs/nfsd/xdr4.h | 1 +
2 files changed, 70 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5f5058e..ea3a231 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -41,6 +41,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"
@@ -1743,11 +1744,58 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
DECODE_TAIL;
}

+static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
+ struct nl4_server *ns)
+{
+ DECODE_HEAD;
+ struct nfs42_netaddr *naddr;
+
+ READ_BUF(4);
+ ns->nl4_type = be32_to_cpup(p++);
+
+ /* currently support for 1 inter-server source server */
+ switch (ns->nl4_type) {
+ case NL4_NAME:
+ case NL4_URL:
+ READ_BUF(4);
+ ns->u.nl4_str_sz = be32_to_cpup(p++);
+ if (ns->u.nl4_str_sz > NFS4_OPAQUE_LIMIT)
+ goto xdr_error;
+
+ READ_BUF(ns->u.nl4_str_sz);
+ COPYMEM(ns->u.nl4_str,
+ ns->u.nl4_str_sz);
+ break;
+ case NL4_NETADDR:
+ naddr = &ns->u.nl4_addr;
+
+ READ_BUF(4);
+ naddr->netid_len = be32_to_cpup(p++);
+ if (naddr->netid_len > RPCBIND_MAXNETIDLEN)
+ goto xdr_error;
+
+ READ_BUF(naddr->netid_len + 4); /* 4 for uaddr len */
+ COPYMEM(naddr->netid, naddr->netid_len);
+
+ naddr->addr_len = be32_to_cpup(p++);
+ if (naddr->addr_len > RPCBIND_MAXUADDRLEN)
+ goto xdr_error;
+
+ READ_BUF(naddr->addr_len);
+ COPYMEM(naddr->addr, naddr->addr_len);
+ break;
+ default:
+ goto xdr_error;
+ }
+ DECODE_TAIL;
+}
+
static __be32
nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
{
DECODE_HEAD;
- unsigned int tmp;
+ struct nl4_server *ns;
+ int i, count;

status = nfsd4_decode_stateid(argp, &copy->cp_src_stateid);
if (status)
@@ -1762,8 +1810,24 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
p = xdr_decode_hyper(p, &copy->cp_count);
copy->cp_consecutive = be32_to_cpup(p++);
copy->cp_synchronous = be32_to_cpup(p++);
- tmp = be32_to_cpup(p); /* Source server list not supported */
+ count = be32_to_cpup(p++);
+
+ if (count == 0) /* intra-server copy */
+ goto intra;

+ /* decode all the supplied server addresses but use first */
+ copy->cp_src = kmalloc(count * sizeof(struct nl4_server), GFP_KERNEL);
+ if (copy->cp_src == NULL)
+ return nfserrno(-ENOMEM);
+
+ ns = copy->cp_src;
+ for (i = 0; i < count; i++) {
+ status = nfsd4_decode_nl4_server(argp, ns);
+ if (status)
+ return status;
+ ns++;
+ }
+intra:
DECODE_TAIL;
}

@@ -4232,6 +4296,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
p = xdr_reserve_space(&resp->xdr, 4 + 4);
*p++ = cpu_to_be32(copy->cp_consecutive);
*p++ = cpu_to_be32(copy->cp_synchronous);
+
+ /* allocated in nfsd4_decode_copy */
+ kfree(copy->cp_src);
return 0;
}

diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 4ccd43b..ac01bc6 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -517,6 +517,7 @@ struct nfsd4_copy {
u64 cp_src_pos;
u64 cp_dst_pos;
u64 cp_count;
+ struct nl4_server *cp_src;

/* both */
bool cp_consecutive;
--
1.8.3.1


2017-09-28 17:30:19

by Olga Kornievskaia

[permalink] [raw]
Subject: [PATCH v4 7/8] NFSD: allow inter server COPY to have a STALE source server fh

The inter server to server COPY source server filehandle
is a foreign filehandle as the COPY is sent to the destination
server.

Signed-off-by: Olga Kornievskaia <[email protected]>
---
fs/nfsd/Kconfig | 10 ++++++++++
fs/nfsd/nfs4proc.c | 45 ++++++++++++++++++++++++++++++++++++++++++---
fs/nfsd/nfsfh.h | 5 ++++-
fs/nfsd/xdr4.h | 1 +
4 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig
index 20b1c17..37ff3d5 100644
--- a/fs/nfsd/Kconfig
+++ b/fs/nfsd/Kconfig
@@ -131,6 +131,16 @@ config NFSD_FLEXFILELAYOUT

If unsure, say N.

+config NFSD_V4_2_INTER_SSC
+ bool "NFSv4.2 inter server to server COPY"
+ depends on NFSD_V4 && NFS_V4_1 && NFS_V4_2
+ help
+ This option enables support for NFSv4.2 inter server to
+ server copy where the destination server calls the NFSv4.2
+ client to read the data to copy from the source server.
+
+ If unsure, say N.
+
config NFSD_V4_SECURITY_LABEL
bool "Provide Security Label support for NFSv4 server"
depends on NFSD_V4 && SECURITY
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index a0536a6..18d1de4 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -516,12 +516,21 @@ static __be32 nfsd4_open_omfg(struct svc_rqst *rqstp, struct nfsd4_compound_stat
union nfsd4_op_u *u)
{
struct nfsd4_putfh *putfh = &u->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);
+#ifdef CONFIG_NFSD_V4_2_INTER_SSC
+ if (ret == nfserr_stale && HAS_CSTATE_FLAG(cstate, NO_VERIFY_FH)) {
+ CLEAR_CSTATE_FLAG(cstate, NO_VERIFY_FH);
+ SET_FH_FLAG(&cstate->current_fh, NFSD4_FH_FOREIGN);
+ ret = 0;
+ }
+#endif
+ return ret;
}

static __be32
@@ -1903,6 +1912,26 @@ static void svcxdr_init_encode(struct svc_rqst *rqstp,
- rqstp->rq_auth_slack;
}

+#ifdef CONFIG_NFSD_V4_2_INTER_SSC
+static bool _compound_contains_inter_copy(struct nfsd4_op *ops, int start,
+ int end)
+{
+ bool found = false;
+ struct nfsd4_copy *copy;
+ int i;
+
+ for (i = start; i < end; i++) {
+ if (ops[i].opnum == OP_COPY) {
+ copy = (struct nfsd4_copy *)&ops[i].u;
+ if (copy->cp_src)
+ found = true;
+ break;
+ }
+ }
+ return found;
+}
+#endif
+
/*
* COMPOUND call.
*/
@@ -1950,6 +1979,14 @@ static void svcxdr_init_encode(struct svc_rqst *rqstp,
while (!status && resp->opcnt < args->opcnt) {
op = &args->ops[resp->opcnt++];

+#ifdef CONFIG_NFSD_V4_2_INTER_SSC
+ if (op->opnum == OP_PUTFH &&
+ args->ops[resp->opcnt].opnum == OP_SAVEFH &&
+ args->ops[resp->opcnt+1].opnum == OP_PUTFH &&
+ _compound_contains_inter_copy(args->ops, resp->opcnt+2,
+ args->opcnt))
+ SET_CSTATE_FLAG(cstate, NO_VERIFY_FH);
+#endif
dprintk("nfsv4 compound op #%d/%d: %d (%s)\n",
resp->opcnt, args->opcnt, op->opnum,
nfsd4_op_name(op->opnum));
@@ -1964,12 +2001,14 @@ static void svcxdr_init_encode(struct svc_rqst *rqstp,
goto encode_op;
}

- if (!current_fh->fh_dentry) {
+ if (!current_fh->fh_dentry &&
+ !HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) {
if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
op->status = nfserr_nofilehandle;
goto encode_op;
}
- } else if (current_fh->fh_export->ex_fslocs.migrated &&
+ } else if (current_fh->fh_export &&
+ current_fh->fh_export->ex_fslocs.migrated &&
!(op->opdesc->op_flags & ALLOWED_ON_ABSENT_FS)) {
op->status = nfserr_moved;
goto encode_op;
diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h
index e47cf6c..eeff20b 100644
--- a/fs/nfsd/nfsfh.h
+++ b/fs/nfsd/nfsfh.h
@@ -33,7 +33,7 @@ static inline ino_t u32_to_ino_t(__u32 uino)

bool fh_locked; /* inode locked by us */
bool fh_want_write; /* remount protection taken */
-
+ int fh_flags; /* FH flags */
#ifdef CONFIG_NFSD_V3
bool fh_post_saved; /* post-op attrs saved */
bool fh_pre_saved; /* pre-op attrs saved */
@@ -54,6 +54,9 @@ static inline ino_t u32_to_ino_t(__u32 uino)
#endif /* CONFIG_NFSD_V3 */

} svc_fh;
+#define NFSD4_FH_FOREIGN (1<<0)
+#define SET_FH_FLAG(c, f) ((c)->fh_flags |= (f))
+#define HAS_FH_FLAG(c, f) ((c)->fh_flags & (f))

enum nfsd_fsid {
FSID_DEV = 0,
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index c979235..82d714b 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -45,6 +45,7 @@

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

#define SET_CSTATE_FLAG(c, f) ((c)->sid_flags |= (f))
#define HAS_CSTATE_FLAG(c, f) ((c)->sid_flags & (f))
--
1.8.3.1


2017-09-28 17:30:26

by Olga Kornievskaia

[permalink] [raw]
Subject: [PATCH v4 5/8] 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 | 7 ++++---
fs/nfsd/xdr4.h | 6 +++---
3 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 0a2bab0..72a0bc5 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -546,9 +546,9 @@ static __be32 nfsd4_open_omfg(struct svc_rqst *rqstp, struct nfsd4_compound_stat
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;
}
@@ -561,9 +561,9 @@ static __be32 nfsd4_open_omfg(struct svc_rqst *rqstp, struct nfsd4_compound_stat
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 e28447d..b11d1a5 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -7226,7 +7226,8 @@ static int nfs4_state_create_net(struct net *net)
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));
}

@@ -7235,14 +7236,14 @@ static int nfs4_state_create_net(struct net *net)
{
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 79f8ed4..c979235 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -46,9 +46,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


2017-09-28 17:30:19

by Olga Kornievskaia

[permalink] [raw]
Subject: [PATCH v4 3/8] NFSD add COPY_NOTIFY operation

Introducing the COPY_NOTIFY operation.

Create a new unique stateid that will keep track of the copy
state and the upcoming READs that will use that stateid. Keep
it in the list associated with parent stateid.

Return single netaddr to advertise to the copy.

Signed-off-by: Andy Adamson <[email protected]>
Signed-off-by: Olga Kornievskaia <[email protected]>
---
fs/nfsd/nfs4proc.c | 63 +++++++++++++++++++++++++++++++++-
fs/nfsd/nfs4state.c | 3 ++
fs/nfsd/nfs4xdr.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++--
fs/nfsd/state.h | 5 ++-
fs/nfsd/xdr4.h | 13 +++++++
5 files changed, 177 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 8d0c87f..0a2bab0 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/sunrpc/addr.h>

#include "idmap.h"
#include "cache.h"
@@ -1263,7 +1264,8 @@ static void nfsd4_do_async_copy(struct work_struct *work)
status = nfserrno(-ENOMEM);
goto out;
}
- copy->cps = nfs4_alloc_init_cp_state(nn, copy->stid);
+ copy->cps = nfs4_alloc_init_cp_state(nn, nn->nfsd4_lease,
+ copy->stid);
if (!copy->cps) {
status = nfserrno(-ENOMEM);
kfree(async_copy);
@@ -1306,6 +1308,44 @@ static void nfsd4_do_async_copy(struct work_struct *work)
}

static __be32
+nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+ union nfsd4_op_u *u)
+{
+ struct nfsd4_copy_notify *cn = &u->copy_notify;
+ __be32 status;
+ struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+ struct nfs4_stid *stid;
+ struct nfs4_cp_state *cps;
+
+ status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
+ &cn->cpn_src_stateid, RD_STATE, NULL,
+ NULL, &stid);
+ if (status)
+ return status;
+
+ cn->cpn_sec = nn->nfsd4_lease;
+ cn->cpn_nsec = 0;
+
+ status = nfserrno(-ENOMEM);
+ cps = nfs4_alloc_init_cp_state(nn, nn->nfsd4_lease, stid);
+ if (!cps)
+ return status;
+ memcpy(&cn->cpn_cnr_stateid, &cps->cp_stateid, sizeof(stateid_t));
+
+ /**
+ * For now, only return one server address in cpn_src, the
+ * address used by the client to connect to this server.
+ */
+ cn->cpn_src.nl4_type = NL4_NETADDR;
+ status = nfsd4_set_netaddr((struct sockaddr *)&rqstp->rq_daddr,
+ &cn->cpn_src.u.nl4_addr);
+ if (status != 0)
+ nfs4_free_cp_state(cps);
+
+ return status;
+}
+
+static __be32
nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_fallocate *fallocate, int flags)
{
@@ -2251,6 +2291,21 @@ static inline u32 nfsd4_offload_status_rsize(struct svc_rqst *rqstp,
1 /* osr_complete<1> optional 0 for now */) * sizeof(__be32);
}

+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 */ +
+ 1 /* cnr_stateid seq */ +
+ op_encode_stateid_maxsz /* cnr_stateid */ +
+ 1 /* num cnr_source_server*/ +
+ 1 /* nl4_type */ +
+ 1 /* nl4 size */ +
+ XDR_QUADLEN(NFS4_OPAQUE_LIMIT) /*nl4_loc + nl4_loc_sz */)
+ * sizeof(__be32);
+}
+
#ifdef CONFIG_NFSD_PNFS
static inline u32 nfsd4_getdeviceinfo_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
{
@@ -2674,6 +2729,12 @@ static inline u32 nfsd4_seek_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
.op_name = "OP_OFFLOAD_CANCEL",
.op_rsize_bop = nfsd4_only_status_rsize,
},
+ [OP_COPY_NOTIFY] = {
+ .op_func = nfsd4_copy_notify,
+ .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
+ .op_name = "OP_COPY_NOTIFY",
+ .op_rsize_bop = nfsd4_copy_notify_rsize,
+ },
};

/**
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f4b193e..9932d50 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -681,6 +681,7 @@ struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *sla
* the source file.
*/
struct nfs4_cp_state *nfs4_alloc_init_cp_state(struct nfsd_net *nn,
+ u64 cpn_sec,
struct nfs4_stid *p_stid)
{
struct nfs4_cp_state *cps;
@@ -700,6 +701,8 @@ struct nfs4_cp_state *nfs4_alloc_init_cp_state(struct nfsd_net *nn,
cps->cp_stateid.si_opaque.so_clid.cl_boot = nn->boot_time;
cps->cp_stateid.si_opaque.so_clid.cl_id = nn->s2s_cp_cl_id;
cps->cp_p_stid = p_stid;
+ cps->cp_active = false;
+ cps->cp_timeout = jiffies + (cpn_sec * HZ);
INIT_LIST_HEAD(&cps->cp_list);
list_add(&cps->cp_list, &p_stid->sc_cp_list);

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index ea3a231..a2d5238 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1839,6 +1839,22 @@ static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
}

static __be32
+nfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp,
+ struct nfsd4_copy_notify *cn)
+{
+ int status;
+
+ status = nfsd4_decode_stateid(argp, &cn->cpn_src_stateid);
+ if (status)
+ return status;
+ status = nfsd4_decode_nl4_server(argp, &cn->cpn_dst);
+ if (status)
+ return status;
+
+ return status;
+}
+
+static __be32
nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
{
DECODE_HEAD;
@@ -1939,7 +1955,7 @@ static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
/* 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,
@@ -4283,6 +4299,45 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
}

static __be32
+nfsd42_encode_nl4_server(struct nfsd4_compoundres *resp, struct nl4_server *ns)
+{
+ struct xdr_stream *xdr = &resp->xdr;
+ struct nfs42_netaddr *addr;
+ __be32 *p;
+
+ p = xdr_reserve_space(xdr, 4);
+ *p++ = cpu_to_be32(ns->nl4_type);
+
+ switch (ns->nl4_type) {
+ case NL4_NETADDR:
+ addr = &ns->u.nl4_addr;
+
+ /** netid_len, netid, uaddr_len, uaddr (port included
+ * in RPCBIND_MAXUADDRLEN)
+ */
+ p = xdr_reserve_space(xdr,
+ 4 /* netid len */ +
+ (XDR_QUADLEN(addr->netid_len) * 4) +
+ 4 /* uaddr len */ +
+ (XDR_QUADLEN(addr->addr_len) * 4));
+ if (!p)
+ return nfserr_resource;
+
+ *p++ = cpu_to_be32(addr->netid_len);
+ p = xdr_encode_opaque_fixed(p, addr->netid,
+ addr->netid_len);
+ *p++ = cpu_to_be32(addr->addr_len);
+ p = xdr_encode_opaque_fixed(p, addr->addr,
+ addr->addr_len);
+ break;
+ default:
+ WARN_ON(ns->nl4_type != NL4_NETADDR);
+ }
+
+ return 0;
+}
+
+static __be32
nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_copy *copy)
{
@@ -4322,6 +4377,44 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
}

static __be32
+nfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr,
+ struct nfsd4_copy_notify *cn)
+{
+ struct xdr_stream *xdr = &resp->xdr;
+ __be32 *p;
+
+ if (nfserr)
+ return nfserr;
+
+ /* 8 sec, 4 nsec */
+ p = xdr_reserve_space(xdr, 12);
+ if (!p)
+ return nfserr_resource;
+
+ /* cnr_lease_time */
+ p = xdr_encode_hyper(p, cn->cpn_sec);
+ *p++ = cpu_to_be32(cn->cpn_nsec);
+
+ /* cnr_stateid */
+ nfserr = nfsd4_encode_stateid(xdr, &cn->cpn_cnr_stateid);
+ if (nfserr)
+ return nfserr;
+
+ /* cnr_src.nl_nsvr */
+ p = xdr_reserve_space(xdr, 4);
+ if (!p)
+ return nfserr_resource;
+
+ *p++ = cpu_to_be32(1);
+
+ nfserr = nfsd42_encode_nl4_server(resp, &cn->cpn_src);
+ if (nfserr)
+ return nfserr;
+
+ return nfserr;
+}
+
+static __be32
nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_seek *seek)
{
@@ -4418,7 +4511,7 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
/* 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/state.h b/fs/nfsd/state.h
index 55f09a9..85b2d8b 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -114,6 +114,8 @@ struct nfs4_cp_state {
bool cp_cancelled; /* copy cancelled */
spinlock_t cp_lock;
ssize_t cp_bytes_copied;/* copy progress */
+ bool cp_active; /* has the copy started */
+ unsigned long cp_timeout; /* copy timeout */
};

/*
@@ -623,7 +625,8 @@ __be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
struct nfs4_stid **s, struct nfsd_net *nn);
struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab,
void (*sc_free)(struct nfs4_stid *));
-struct nfs4_cp_state *nfs4_alloc_init_cp_state(struct nfsd_net *nn, struct nfs4_stid *p_stid);
+struct nfs4_cp_state *nfs4_alloc_init_cp_state(struct nfsd_net *nn, u64 cpn_sec,
+ struct nfs4_stid *p_stid);
void nfs4_free_cp_state(struct nfs4_cp_state *cps);
void nfs4_unhash_stid(struct nfs4_stid *s);
void nfs4_put_stid(struct nfs4_stid *s);
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index ac01bc6..79f8ed4 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -561,6 +561,18 @@ struct nfsd4_offload_status {
u32 status;
};

+struct nfsd4_copy_notify {
+ /* request */
+ stateid_t cpn_src_stateid;
+ struct nl4_server cpn_dst;
+
+ /* response */
+ stateid_t cpn_cnr_stateid;
+ u64 cpn_sec;
+ u32 cpn_nsec;
+ struct nl4_server cpn_src;
+};
+
struct nfsd4_op {
int opnum;
const struct nfsd4_operation * opdesc;
@@ -620,6 +632,7 @@ struct nfsd4_op {
struct nfsd4_clone clone;
struct nfsd4_copy copy;
struct nfsd4_offload_status offload_status;
+ struct nfsd4_copy_notify copy_notify;
struct nfsd4_seek seek;
} u;
struct nfs4_replay * replay;
--
1.8.3.1


2017-09-28 17:30:19

by Olga Kornievskaia

[permalink] [raw]
Subject: [PATCH v4 6/8] nfsd: remove unnecessary nofilehandle checks

These checks should have already be done centrally in
nfsd4_proc_compound, the checks in each individual operation are
unnecessary.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4proc.c | 14 ++------------
1 file changed, 2 insertions(+), 12 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 72a0bc5..a0536a6 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -507,9 +507,6 @@ static __be32 nfsd4_open_omfg(struct svc_rqst *rqstp, struct nfsd4_compound_stat
nfsd4_getfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
- if (!cstate->current_fh.fh_dentry)
- return nfserr_nofilehandle;
-
u->getfh = &cstate->current_fh;
return nfs_ok;
}
@@ -557,9 +554,6 @@ static __be32 nfsd4_open_omfg(struct svc_rqst *rqstp, struct nfsd4_compound_stat
nfsd4_savefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
- if (!cstate->current_fh.fh_dentry)
- return nfserr_nofilehandle;
-
fh_dup2(&cstate->save_fh, &cstate->current_fh);
if (HAS_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG)) {
memcpy(&cstate->save_stateid, &cstate->current_stateid, sizeof(stateid_t));
@@ -725,10 +719,8 @@ static void gen_boot_verifier(nfs4_verifier *verifier, struct net *net)
union nfsd4_op_u *u)
{
struct nfsd4_link *link = &u->link;
- __be32 status = nfserr_nofilehandle;
+ __be32 status;

- if (!cstate->save_fh.fh_dentry)
- return status;
status = nfsd_link(rqstp, &cstate->current_fh,
link->li_name, link->li_namelen, &cstate->save_fh);
if (!status)
@@ -873,10 +865,8 @@ static __be32 nfsd4_do_lookupp(struct svc_rqst *rqstp, struct svc_fh *fh)
union nfsd4_op_u *u)
{
struct nfsd4_rename *rename = &u->rename;
- __be32 status = nfserr_nofilehandle;
+ __be32 status;

- if (!cstate->save_fh.fh_dentry)
- return status;
if (opens_in_grace(SVC_NET(rqstp)) &&
!(cstate->save_fh.fh_export->ex_flags & NFSEXP_NOSUBTREECHECK))
return nfserr_grace;
--
1.8.3.1