From: NeilBrown <neilb@cse.unsw.edu.au>

From: "J. Bruce Fields" <bfields@fieldses.org>

From: Andros: Implement server-side reboot recovery (server now handles
open and lock reclaims).  Not completely to spec: we don't yet store the
state in stable storage that would be required to recover correctly in
certain situations.


---

 25-akpm/fs/nfsd/nfs4proc.c         |  102 +++++++++++++++++++++++++++++++------
 25-akpm/fs/nfsd/nfs4state.c        |   53 +++++++++++++++++--
 25-akpm/include/linux/nfsd/nfsd.h  |    3 +
 25-akpm/include/linux/nfsd/state.h |    2 
 4 files changed, 140 insertions(+), 20 deletions(-)

diff -puN fs/nfsd/nfs4proc.c~kNFSdv4-10-of-10-Implement-server-side-reboot-recovery-mostly fs/nfsd/nfs4proc.c
--- 25/fs/nfsd/nfs4proc.c~kNFSdv4-10-of-10-Implement-server-side-reboot-recovery-mostly	Thu Apr  8 15:56:38 2004
+++ 25-akpm/fs/nfsd/nfs4proc.c	Thu Apr  8 15:56:38 2004
@@ -66,10 +66,31 @@ fh_dup2(struct svc_fh *dst, struct svc_f
 }
 
 static int
+do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
+{
+	int accmode, status;
+
+	if (open->op_truncate &&
+		!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE))
+		return nfserr_inval;
+
+	accmode = MAY_NOP;
+	if (open->op_share_access & NFS4_SHARE_ACCESS_READ)
+		accmode = MAY_READ;
+	if (open->op_share_deny & NFS4_SHARE_ACCESS_WRITE)
+		accmode |= (MAY_WRITE | MAY_TRUNC);
+	accmode |= MAY_OWNER_OVERRIDE;
+
+	status = fh_verify(rqstp, current_fh, S_IFREG, accmode);
+
+	return status;
+}
+
+static int
 do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
 {
 	struct svc_fh resfh;
-	int accmode, status;
+	int status;
 
 	fh_init(&resfh, NFS4_FHSIZE);
 	open->op_truncate = 0;
@@ -92,6 +113,8 @@ do_open_lookup(struct svc_rqst *rqstp, s
 
 	if (!status) {
 		set_change_info(&open->op_cinfo, current_fh);
+
+		/* set reply cache */
 		fh_dup2(current_fh, &resfh);
 		/* XXXJBF: keep a saved svc_fh struct instead?? */
 		open->op_stateowner->so_replay.rp_openfh_len =
@@ -100,19 +123,41 @@ do_open_lookup(struct svc_rqst *rqstp, s
 				&resfh.fh_handle.fh_base,
 				resfh.fh_handle.fh_size);
 
-		accmode = MAY_NOP;
-		if (open->op_share_access & NFS4_SHARE_ACCESS_READ)
-			accmode = MAY_READ;
-		if (open->op_share_deny & NFS4_SHARE_ACCESS_WRITE)
-			accmode |= (MAY_WRITE | MAY_TRUNC);
-		accmode |= MAY_OWNER_OVERRIDE;
-		status = fh_verify(rqstp, current_fh, S_IFREG, accmode);
+		status = do_open_permission(rqstp, current_fh, open);
 	}
 
 	fh_put(&resfh);
 	return status;
 }
 
+static int
+do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
+{
+	int status;
+
+	dprintk("NFSD: do_open_fhandle\n");
+
+	/* we don't know the target directory, and therefore can not
+	* set the change info
+	*/
+
+	memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info));
+
+	/* set replay cache */
+	open->op_stateowner->so_replay.rp_openfh_len = current_fh->fh_handle.fh_size;
+	memcpy(open->op_stateowner->so_replay.rp_openfh,
+		&current_fh->fh_handle.fh_base,
+		current_fh->fh_handle.fh_size);
+
+	open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) &&
+	!open->op_iattr.ia_size;
+
+	status = do_open_permission(rqstp, current_fh, open);
+
+	return status;
+}
+
+
 /*
  * nfs4_unlock_state() called in encode
  */
@@ -124,6 +169,13 @@ nfsd4_open(struct svc_rqst *rqstp, struc
 		(int)open->op_fname.len, open->op_fname.data,
 		open->op_stateowner);
 
+	if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
+		return nfserr_grace;
+
+	if (nfs4_in_no_grace() &&
+		           open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
+		return nfserr_no_grace;
+
 	/* This check required by spec. */
 	if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
 		return nfserr_inval;
@@ -148,16 +200,30 @@ nfsd4_open(struct svc_rqst *rqstp, struc
 	}
 	if (status)
 		return status;
+	if (open->op_claim_type == NFS4_OPEN_CLAIM_NULL) {
 	/*
 	 * This block of code will (1) set CURRENT_FH to the file being opened,
 	 * creating it if necessary, (2) set open->op_cinfo, 
 	 * (3) set open->op_truncate if the file is to be truncated 
 	 * after opening, (4) do permission checking.
 	 */
-	status = do_open_lookup(rqstp, current_fh, open);
-	if (status)
-		return status;
-
+		status = do_open_lookup(rqstp, current_fh, open);
+		if (status)
+			return status;
+	} else if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) {
+	/*
+	* The CURRENT_FH is already set to the file being opened. This
+	* block of code will (1) set open->op_cinfo, (2) set
+	* open->op_truncate if the file is to be truncated after opening,
+	* (3) do permission checking.
+	*/
+		status = do_open_fhandle(rqstp, current_fh, open);
+		if (status)
+			return status;
+	} else {
+		printk("NFSD: unsupported OPEN claim type\n");
+		return nfserr_inval;
+	}
 	/*
 	 * nfsd4_process_open2() does the actual opening of the file.  If
 	 * successful, it (1) truncates the file if open->op_truncate was
@@ -414,6 +480,8 @@ nfsd4_read(struct svc_rqst *rqstp, struc
 	int status;
 
 	/* no need to check permission - this will be done in nfsd_read() */
+	if (nfs4_in_grace())
+		return nfserr_grace;
 
 	if (read->rd_offset >= OFFSET_MAX)
 		return nfserr_inval;
@@ -537,10 +605,13 @@ static inline int
 nfsd4_setattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_setattr *setattr)
 {
 	struct nfs4_stateid *stp;
-	int status = nfserr_nofilehandle;
+	int status = nfs_ok;
+
+	if (nfs4_in_grace())
+		return nfserr_grace;
 
 	if (!current_fh->fh_dentry)
-		goto out;
+		return nfserr_nofilehandle;
 
 	status = nfs_ok;
 	if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
@@ -579,6 +650,9 @@ nfsd4_write(struct svc_rqst *rqstp, stru
 	u32 *p;
 	int status = nfs_ok;
 
+	if (nfs4_in_grace())
+		return nfserr_grace;
+
 	/* no need to check permission - this will be done in nfsd_write() */
 
 	if (write->wr_offset >= OFFSET_MAX)
diff -puN fs/nfsd/nfs4state.c~kNFSdv4-10-of-10-Implement-server-side-reboot-recovery-mostly fs/nfsd/nfs4state.c
--- 25/fs/nfsd/nfs4state.c~kNFSdv4-10-of-10-Implement-server-side-reboot-recovery-mostly	Thu Apr  8 15:56:38 2004
+++ 25-akpm/fs/nfsd/nfs4state.c	Thu Apr  8 15:56:38 2004
@@ -52,6 +52,7 @@
 
 /* Globals */
 time_t boot_time;
+static time_t grace_end = 0;
 static u32 current_clientid = 1;
 static u32 current_ownerid;
 static u32 current_fileid;
@@ -1090,7 +1091,7 @@ nfsd4_process_open1(struct nfsd4_open *o
 
 	status = nfserr_stale_clientid;
 	if (STALE_CLIENTID(&open->op_clientid))
-		goto out;
+		return status;
 
 	strhashval = ownerstr_hashval(clientid->cl_id, open->op_owner);
 	if (find_openstateowner_str(strhashval, open, &sop)) {
@@ -1111,7 +1112,7 @@ nfsd4_process_open1(struct nfsd4_open *o
 			}
 			/* replay: indicate to calling function */
 			status = NFSERR_REPLAY_ME;
-			goto out;
+			return status;
 		}
 		if (sop->so_confirmed) {
 			if (open->op_seqid == sop->so_seqid + 1) { 
@@ -1149,6 +1150,8 @@ instantiate_new_owner:
 renew:
 	renew_client(sop->so_client);
 out:
+	if (status && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
+		status = nfserr_reclaim_bad;
 	return status;
 }
 /*
@@ -1159,7 +1162,7 @@ nfsd4_process_open2(struct svc_rqst *rqs
 {
 	struct iattr iattr;
 	struct nfs4_stateowner *sop = open->op_stateowner;
-	struct nfs4_file *fp;
+	struct nfs4_file *fp = NULL;
 	struct inode *ino;
 	unsigned int fi_hashval;
 	struct nfs4_stateid *stq, *stp = NULL;
@@ -1167,7 +1170,7 @@ nfsd4_process_open2(struct svc_rqst *rqs
 
 	status = nfserr_resource;
 	if (!sop)
-		goto out;
+		return status;
 
 	ino = current_fh->fh_dentry->d_inode;
 
@@ -1258,6 +1261,17 @@ out:
 	if (fp && list_empty(&fp->fi_perfile))
 		release_file(fp);
 
+	if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) {
+		if (status)
+			status = nfserr_reclaim_bad;
+		else {
+		/* successful reclaim. so_seqid is decremented because
+		* it will be bumped in encode_open
+		*/
+			open->op_stateowner->so_confirmed = 1;
+			open->op_stateowner->so_seqid--;
+		}
+	}
 	/*
 	* To finish the open response, we just need to set the rflags.
 	*/
@@ -1270,6 +1284,7 @@ out_free:
 	kfree(stp);
 	goto out;
 }
+
 static struct work_struct laundromat_work;
 static void laundromat_main(void *);
 static DECLARE_WORK(laundromat_work, laundromat_main, NULL);
@@ -1954,6 +1969,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struc
 		(long long) lock->lk_offset,
 		(long long) lock->lk_length);
 
+	if (nfs4_in_grace() && !lock->lk_reclaim)
+		return nfserr_grace;
+	if (nfs4_in_no_grace() && lock->lk_reclaim)
+		return nfserr_no_grace;
+
 	if (check_lock_length(lock->lk_offset, lock->lk_length))
 		 return nfserr_inval;
 
@@ -1983,8 +2003,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struc
 		                        CHECK_FH | OPEN_STATE,
 		                        &open_sop, &open_stp,
 					&lock->v.new.clientid);
-		if (status)
+		if (status) {
+			if (lock->lk_reclaim)
+				status = nfserr_reclaim_bad;
 			goto out;
+		}
 		/* create lockowner and lock stateid */
 		fp = open_stp->st_file;
 		strhashval = lock_ownerstr_hashval(fp->fi_inode, 
@@ -2119,6 +2142,9 @@ nfsd4_lockt(struct svc_rqst *rqstp, stru
 	unsigned int strhashval;
 	int status;
 
+	if (nfs4_in_grace())
+		return nfserr_grace;
+
 	if (check_lock_length(lockt->lt_offset, lockt->lt_length))
 		 return nfserr_inval;
 
@@ -2343,6 +2369,7 @@ void 
 nfs4_state_init(void)
 {
 	int i;
+	time_t start = get_seconds();
 
 	if (nfs4_init)
 		return;
@@ -2373,13 +2400,27 @@ nfs4_state_init(void)
 	INIT_LIST_HEAD(&close_lru);
 	INIT_LIST_HEAD(&client_lru);
 	init_MUTEX(&client_sema);
-	boot_time = get_seconds();
+	boot_time = start;
+	grace_end = start + NFSD_LEASE_TIME;
 	INIT_WORK(&laundromat_work,laundromat_main, NULL);
 	schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ);
 	nfs4_init = 1;
 
 }
 
+int
+nfs4_in_grace(void)
+{
+	return time_before(get_seconds(), (unsigned long)grace_end);
+}
+
+int
+nfs4_in_no_grace(void)
+{
+	return (grace_end < get_seconds());
+}
+
+
 static void
 __nfs4_state_shutdown(void)
 {
diff -puN include/linux/nfsd/nfsd.h~kNFSdv4-10-of-10-Implement-server-side-reboot-recovery-mostly include/linux/nfsd/nfsd.h
--- 25/include/linux/nfsd/nfsd.h~kNFSdv4-10-of-10-Implement-server-side-reboot-recovery-mostly	Thu Apr  8 15:56:38 2004
+++ 25-akpm/include/linux/nfsd/nfsd.h	Thu Apr  8 15:57:03 2004
@@ -196,6 +196,9 @@ void		nfsd_lockd_shutdown(void);
 #define	nfserr_openmode		__constant_htonl(NFSERR_OPENMODE)
 #define	nfserr_locks_held	__constant_htonl(NFSERR_LOCKS_HELD)
 #define	nfserr_op_illegal	__constant_htonl(NFSERR_OP_ILLEGAL)
+#define	nfserr_grace		__constant_htonl(NFSERR_GRACE)
+#define	nfserr_no_grace		__constant_htonl(NFSERR_NO_GRACE)
+#define	nfserr_reclaim_bad	__constant_htonl(NFSERR_RECLAIM_BAD)
 
 /* error codes for internal use */
 /* if a request fails due to kmalloc failure, it gets dropped.
diff -puN include/linux/nfsd/state.h~kNFSdv4-10-of-10-Implement-server-side-reboot-recovery-mostly include/linux/nfsd/state.h
--- 25/include/linux/nfsd/state.h~kNFSdv4-10-of-10-Implement-server-side-reboot-recovery-mostly	Thu Apr  8 15:56:38 2004
+++ 25-akpm/include/linux/nfsd/state.h	Thu Apr  8 15:56:38 2004
@@ -215,4 +215,6 @@ extern int nfs4_share_conflict(struct sv
 		unsigned int deny_type);
 extern void nfs4_lock_state(void);
 extern void nfs4_unlock_state(void);
+extern int nfs4_in_grace(void);
+extern int nfs4_in_no_grace(void);
 #endif   /* NFSD4_STATE_H */

_