From: Trond Myklebust <trond.myklebust@fys.uio.no>

NFSv4: Preparation for the server reboot recovery code.


---

 fs/nfs/nfs4proc.c       |   55 +++++++++++++++++++++++++--
 fs/nfs/nfs4state.c      |   89 ++++++++++++++++++++++++++++++++++++++++++++
 fs/nfs/nfs4xdr.c        |   97 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/nfs4.h    |    1 
 include/linux/nfs_fs.h  |    2 
 include/linux/nfs_xdr.h |   13 ++++++
 6 files changed, 253 insertions(+), 4 deletions(-)

diff -puN fs/nfs/nfs4proc.c~nfs-23-open_reclaim fs/nfs/nfs4proc.c
--- 25/fs/nfs/nfs4proc.c~nfs-23-open_reclaim	2004-01-14 02:09:55.000000000 -0800
+++ 25-akpm/fs/nfs/nfs4proc.c	2004-01-14 02:09:55.000000000 -0800
@@ -458,6 +458,54 @@ process_cinfo(struct nfs4_change_info *i
 	}
 }
 
+/*
+ * OPEN_RECLAIM:
+ * 	reclaim state on the server after a reboot.
+ * 	Assumes caller is holding the sp->so_sem
+ */
+int
+nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state)
+{
+	struct inode *inode = state->inode;
+	struct nfs_server *server = NFS_SERVER(inode);
+	struct nfs_fattr fattr = {
+		.valid = 0,
+	};
+	struct nfs4_change_info d_cinfo;
+	struct nfs4_getattr     f_getattr = {
+		.gt_bmval       = nfs4_fattr_bitmap,
+		.gt_attrs       = &fattr,
+	};
+
+	struct nfs_open_reclaimargs o_arg = {
+		.fh = NFS_FH(inode),
+		.seqid = sp->so_seqid,
+		.id = sp->so_id,
+		.share_access = state->state,
+		.clientid = server->nfs4_state->cl_clientid,
+		.claim = NFS4_OPEN_CLAIM_PREVIOUS,
+		.f_getattr = &f_getattr,
+	};
+	struct nfs_openres o_res = {
+		.cinfo = &d_cinfo,
+		.f_getattr = &f_getattr,
+		.server = server,	/* Grrr */
+	};
+	struct rpc_message msg = {
+		.rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_OPEN_RECLAIM],
+		.rpc_argp       = &o_arg,
+		.rpc_resp	= &o_res,
+		.rpc_cred	= sp->so_cred,
+	};
+	int status;
+
+	status = rpc_call_sync(server->client, &msg, 0);
+	nfs4_increment_seqid(status, sp);
+	/* Update the inode attributes */
+	nfs_refresh_inode(inode, &fattr);
+	return status;
+}
+
 struct nfs4_state *
 nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred)
 {
@@ -523,10 +571,9 @@ nfs4_do_open(struct inode *dir, struct q
 	o_arg.id = sp->so_id;
 
 	status = rpc_call_sync(server->client, &msg, 0);
-	if (status) {
-		goto out_up;
-	}
 	nfs4_increment_seqid(status, sp);
+	if (status)
+		goto out_up;
 	process_cinfo(&d_cinfo, &d_attr);
 	nfs_refresh_inode(dir, &d_attr);
 
@@ -555,9 +602,9 @@ nfs4_do_open(struct inode *dir, struct q
 
 		memcpy(&oc_arg.stateid, &o_res.stateid, sizeof(oc_arg.stateid));
 		status = rpc_call_sync(server->client, &msg, 0);
+		nfs4_increment_seqid(status, sp);
 		if (status)
 			goto out_up;
-		nfs4_increment_seqid(status, sp);
 		memcpy(&state->stateid, &oc_res.stateid, sizeof(state->stateid));
 	} else
 		memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid));
diff -puN fs/nfs/nfs4state.c~nfs-23-open_reclaim fs/nfs/nfs4state.c
--- 25/fs/nfs/nfs4state.c~nfs-23-open_reclaim	2004-01-14 02:09:55.000000000 -0800
+++ 25-akpm/fs/nfs/nfs4state.c	2004-01-14 02:09:55.000000000 -0800
@@ -382,6 +382,95 @@ nfs4_increment_seqid(int status, struct 
 		sp->so_seqid++;
 }
 
+static int reclaimer(void *);
+struct reclaimer_args {
+	struct nfs4_client *clp;
+	struct completion complete;
+};
+
+/*
+ * State recovery routine
+ */
+void
+nfs4_recover_state(struct nfs4_client *clp)
+{
+	struct reclaimer_args args = {
+		.clp = clp,
+	};
+	init_completion(&args.complete);
+
+	down_read(&clp->cl_sem);
+	if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0)
+		goto out_failed;
+	wait_for_completion(&args.complete);
+	return;
+out_failed:
+	up_read(&clp->cl_sem);
+}
+
+static void
+nfs4_reclaim_open_state(struct nfs4_state_owner *sp)
+{
+	struct nfs4_state *state;
+	int status;
+
+	list_for_each_entry(state, &sp->so_states, open_states) {
+		status = nfs4_open_reclaim(sp, state);
+		if (status) {
+			/*
+			 * Open state on this file cannot be recovered
+			 * All we can do is revert to using the zero stateid.
+			 */
+			memset(state->stateid.data, 0,
+					sizeof(state->stateid.data));
+			/* Mark the file as being 'closed' */
+			state->state = 0;
+		}
+	}
+}
+
+static int
+reclaimer(void *ptr)
+{
+	struct reclaimer_args *args = (struct reclaimer_args *)ptr;
+	struct nfs4_client *clp = args->clp;
+	struct nfs4_state_owner *sp;
+	int status;
+
+	daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr));
+	allow_signal(SIGKILL);
+
+	complete(&args->complete);
+
+	/* Are there any NFS mounts out there? */
+	if (list_empty(&clp->cl_superblocks))
+		goto out;
+	status = nfs4_proc_setclientid(clp, 0, 0);
+	if (status)
+		goto out_error;
+	status = nfs4_proc_setclientid_confirm(clp);
+	if (status)
+		goto out_error;
+	spin_lock(&clp->cl_lock);
+	list_for_each_entry(sp, &clp->cl_state_owners, so_list) {
+		atomic_inc(&sp->so_count);
+		spin_unlock(&clp->cl_lock);
+		down(&sp->so_sema);
+		nfs4_reclaim_open_state(sp);
+		up(&sp->so_sema);
+		nfs4_put_state_owner(sp);
+		spin_lock(&clp->cl_lock);
+	}
+	spin_unlock(&clp->cl_lock);
+out:
+	up_read(&clp->cl_sem);
+	return 0;
+out_error:
+	printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u\n",
+				NIPQUAD(clp->cl_addr.s_addr));
+	goto out;
+}
+
 /*
  * Local variables:
  *  c-basic-offset: 8
diff -puN fs/nfs/nfs4xdr.c~nfs-23-open_reclaim fs/nfs/nfs4xdr.c
--- 25/fs/nfs/nfs4xdr.c~nfs-23-open_reclaim	2004-01-14 02:09:55.000000000 -0800
+++ 25-akpm/fs/nfs/nfs4xdr.c	2004-01-14 02:09:55.000000000 -0800
@@ -166,6 +166,16 @@ static int nfs_stat_to_errno(int);
 #define NFS4_dec_open_confirm_sz        compound_decode_hdr_maxsz + \
                                         decode_putfh_maxsz + \
                                         op_decode_hdr_maxsz + 4
+#define NFS4_enc_open_reclaim_sz	compound_encode_hdr_maxsz + \
+					encode_putfh_maxsz + \
+					op_encode_hdr_maxsz + \
+					11 + \
+					encode_getattr_maxsz
+#define NFS4_dec_open_reclaim_sz	compound_decode_hdr_maxsz + \
+					decode_putfh_maxsz + \
+					op_decode_hdr_maxsz + \
+					4 + 5 + 2 + 3 + \
+					decode_getattr_maxsz
 #define NFS4_enc_close_sz       compound_encode_hdr_maxsz + \
                                 encode_putfh_maxsz + \
                                 op_encode_hdr_maxsz + 5
@@ -667,6 +677,41 @@ encode_open_confirm(struct xdr_stream *x
 
 
 static int
+encode_open_reclaim(struct xdr_stream *xdr, struct nfs_open_reclaimargs *arg)
+{
+	uint32_t *p;
+
+ /*
+ * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
+ * owner 4, opentype 4, claim 4, delegation_type 4 = 44
+ */
+	RESERVE_SPACE(44);
+	WRITE32(OP_OPEN);
+	WRITE32(arg->seqid);
+	switch (arg->share_access) {
+		case FMODE_READ:
+			WRITE32(NFS4_SHARE_ACCESS_READ);
+			break;
+		case FMODE_WRITE:
+			WRITE32(NFS4_SHARE_ACCESS_WRITE);
+			break;
+		case FMODE_READ|FMODE_WRITE:
+			WRITE32(NFS4_SHARE_ACCESS_BOTH);
+			break;
+		default:
+			BUG();
+	}
+	WRITE32(0);                  /* for linux, share_deny = 0 always */
+	WRITE64(arg->clientid);
+	WRITE32(4);
+	WRITE32(arg->id);
+	WRITE32(NFS4_OPEN_NOCREATE);
+	WRITE32(NFS4_OPEN_CLAIM_PREVIOUS);
+	WRITE32(NFS4_OPEN_DELEGATE_NONE);
+	return 0;
+}
+
+static int
 encode_putfh(struct xdr_stream *xdr, struct nfs_fh *fh)
 {
 	int len = fh->size;
@@ -1058,6 +1103,32 @@ out:
 	return status;
 }
 
+/*
+ * Encode an OPEN request
+ */
+static int
+nfs4_xdr_enc_open_reclaim(struct rpc_rqst *req, uint32_t *p,
+		struct nfs_open_reclaimargs *args)
+{
+	struct xdr_stream xdr;
+	struct compound_hdr hdr = {
+		.nops   = 3,
+	};
+	int status;
+
+	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+	encode_compound_hdr(&xdr, &hdr);
+	status = encode_putfh(&xdr, args->fh);
+	if (status)
+		goto out;
+	status = encode_open_reclaim(&xdr, args);
+	if (status)
+		goto out;
+	status = encode_getattr(&xdr, args->f_getattr);
+out:
+	return status;
+}
+
 
 /*
  * Encode a READ request
@@ -2418,6 +2489,31 @@ out:
 }
 
 /*
+ * Decode OPEN_RECLAIM response
+ */
+static int
+nfs4_xdr_dec_open_reclaim(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *res)
+{
+        struct xdr_stream xdr;
+        struct compound_hdr hdr;
+        int status;
+
+        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+        status = decode_compound_hdr(&xdr, &hdr);
+        if (status)
+                goto out;
+        status = decode_putfh(&xdr);
+        if (status)
+                goto out;
+        status = decode_open(&xdr, res);
+        if (status)
+                goto out;
+        status = decode_getattr(&xdr, res->f_getattr, res->server);
+out:
+        return status;
+}
+
+/*
  * Decode SETATTR response
  */
 static int
@@ -2730,6 +2826,7 @@ struct rpc_procinfo	nfs4_procedures[] = 
   PROC(COMMIT,		enc_commit,	dec_commit),
   PROC(OPEN,		enc_open,	dec_open),
   PROC(OPEN_CONFIRM,	enc_open_confirm,	dec_open_confirm),
+  PROC(OPEN_RECLAIM,	enc_open_reclaim,	dec_open_reclaim),
   PROC(CLOSE,		enc_close,	dec_close),
   PROC(SETATTR,		enc_setattr,	dec_setattr),
   PROC(FSINFO,		enc_fsinfo,	dec_fsinfo),
diff -puN include/linux/nfs4.h~nfs-23-open_reclaim include/linux/nfs4.h
--- 25/include/linux/nfs4.h~nfs-23-open_reclaim	2004-01-14 02:09:55.000000000 -0800
+++ 25-akpm/include/linux/nfs4.h	2004-01-14 02:09:55.000000000 -0800
@@ -289,6 +289,7 @@ enum {
 	NFSPROC4_CLNT_COMMIT,
 	NFSPROC4_CLNT_OPEN,
 	NFSPROC4_CLNT_OPEN_CONFIRM,
+	NFSPROC4_CLNT_OPEN_RECLAIM,
 	NFSPROC4_CLNT_CLOSE,
 	NFSPROC4_CLNT_SETATTR,
 	NFSPROC4_CLNT_FSINFO,
diff -puN include/linux/nfs_fs.h~nfs-23-open_reclaim include/linux/nfs_fs.h
--- 25/include/linux/nfs_fs.h~nfs-23-open_reclaim	2004-01-14 02:09:55.000000000 -0800
+++ 25-akpm/include/linux/nfs_fs.h	2004-01-14 02:09:55.000000000 -0800
@@ -554,6 +554,7 @@ struct nfs4_state {
 /* nfs4proc.c */
 extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short);
 extern int nfs4_proc_setclientid_confirm(struct nfs4_client *);
+extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *);
 extern int nfs4_proc_async_renew(struct nfs4_client *);
 extern int nfs4_do_close(struct inode *, struct nfs4_state *);
 
@@ -572,6 +573,7 @@ extern void nfs4_put_state_owner(struct 
 extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
 extern void nfs4_put_open_state(struct nfs4_state *);
 extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp);
+extern void nfs4_recover_state(struct nfs4_client *);
 
 struct nfs4_mount_data;
 #else
diff -puN include/linux/nfs_xdr.h~nfs-23-open_reclaim include/linux/nfs_xdr.h
--- 25/include/linux/nfs_xdr.h~nfs-23-open_reclaim	2004-01-14 02:09:55.000000000 -0800
+++ 25-akpm/include/linux/nfs_xdr.h	2004-01-14 02:09:55.000000000 -0800
@@ -134,6 +134,19 @@ struct nfs_open_confirmres {
 };
 
 /*
+ * Arguments to the open_reclaim call.
+ */
+struct nfs_open_reclaimargs {
+	struct nfs_fh *		fh;
+	__u64			clientid;
+	__u32			seqid;
+	__u32			id;
+	__u32			share_access;
+	__u32			claim;
+	struct nfs4_getattr *   f_getattr;
+};
+
+/*
  * Arguments to the close call.
  */
 struct nfs_closeargs {

_