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

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

From: Andros: Idea is to keep around a list of openowners recently released
by closes, and make sure they stay around long enough so that replays still
work.


---

 25-akpm/fs/nfsd/nfs4state.c        |   85 +++++++++++++++++++++++++++----------
 25-akpm/include/linux/nfsd/state.h |    6 ++
 2 files changed, 69 insertions(+), 22 deletions(-)

diff -puN fs/nfsd/nfs4state.c~kNFSdv4-6-of-10-Keep-state-to-allow-replays-for-close-to-work. fs/nfsd/nfs4state.c
--- 25/fs/nfsd/nfs4state.c~kNFSdv4-6-of-10-Keep-state-to-allow-replays-for-close-to-work.	2004-04-07 19:39:37.855308840 -0700
+++ 25-akpm/fs/nfsd/nfs4state.c	2004-04-07 19:39:37.862307776 -0700
@@ -136,12 +136,16 @@ static void release_file(struct nfs4_fil
  *
  * client_lru holds client queue ordered by nfs4_client.cl_time
  * for lease renewal.
+ *
+ * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time
+ * for last close replay.
  */
 static struct list_head	conf_id_hashtbl[CLIENT_HASH_SIZE];
 static struct list_head	conf_str_hashtbl[CLIENT_HASH_SIZE];
 static struct list_head	unconf_str_hashtbl[CLIENT_HASH_SIZE];
 static struct list_head	unconf_id_hashtbl[CLIENT_HASH_SIZE];
 static struct list_head client_lru;
+static struct list_head close_lru;
 
 static inline void
 renew_client(struct nfs4_client *clp)
@@ -774,6 +778,8 @@ alloc_init_open_stateowner(unsigned int 
 	INIT_LIST_HEAD(&sop->so_perclient);
 	INIT_LIST_HEAD(&sop->so_perfilestate);
 	INIT_LIST_HEAD(&sop->so_perlockowner);  /* not used */
+	INIT_LIST_HEAD(&sop->so_close_lru);
+	sop->so_time = 0;
 	list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]);
 	list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]);
 	list_add(&sop->so_perclient, &clp->cl_perclient);
@@ -814,6 +820,7 @@ release_stateowner(struct nfs4_stateowne
 	list_del(&sop->so_strhash);
 	list_del(&sop->so_perclient);
 	list_del(&sop->so_perlockowner);
+	list_del(&sop->so_close_lru);
 	del_perclient++;
 	while (!list_empty(&sop->so_perfilestate)) {
 		stp = list_entry(sop->so_perfilestate.next, 
@@ -882,6 +889,19 @@ release_file(struct nfs4_file *fp)
 }	
 
 void
+move_to_close_lru(struct nfs4_stateowner *sop)
+{
+	dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop);
+	/* remove stateowner from all other hash lists except perclient */
+	list_del_init(&sop->so_idhash);
+	list_del_init(&sop->so_strhash);
+	list_del_init(&sop->so_perlockowner);
+
+        list_add_tail(&sop->so_close_lru, &close_lru);
+        sop->so_time = get_seconds();
+}
+
+void
 release_state_owner(struct nfs4_stateid *stp, struct nfs4_stateowner **sopp,
 		int flag)
 {
@@ -890,16 +910,13 @@ release_state_owner(struct nfs4_stateid 
 
 	dprintk("NFSD: release_state_owner\n");
 	release_stateid(stp, flag);
-	/*
-	 * release unused nfs4_stateowners.
-	 * XXX will need to be placed  on an  open_stateid_lru list to be
+
+	/* place unused nfs4_stateowners on so_close_lru list to be
 	 * released by the laundromat service after the lease period
 	 * to enable us to handle CLOSE replay
 	 */
-	if (sop->so_confirmed && list_empty(&sop->so_perfilestate)) {
-		release_stateowner(sop);
-		*sopp = NULL;
-	}
+	if (sop->so_confirmed && list_empty(&sop->so_perfilestate))
+		move_to_close_lru(sop);
 	/* unused nfs4_file's are releseed. XXX slab cache? */
 	if (list_empty(&fp->fi_perfile)) {
 		release_file(fp);
@@ -1316,9 +1333,11 @@ time_t
 nfs4_laundromat(void)
 {
 	struct nfs4_client *clp;
+	struct nfs4_stateowner *sop;
 	struct list_head *pos, *next;
 	time_t cutoff = get_seconds() - NFSD_LEASE_TIME;
-	time_t t, return_val = NFSD_LEASE_TIME;
+	time_t t, clientid_val = NFSD_LEASE_TIME;
+	time_t u, close_val = NFSD_LEASE_TIME;
 
 	nfs4_lock_state();
 
@@ -1327,18 +1346,30 @@ nfs4_laundromat(void)
 		clp = list_entry(pos, struct nfs4_client, cl_lru);
 		if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
 			t = clp->cl_time - cutoff;
-			if (return_val > t)
-				return_val = t;
+			if (clientid_val > t)
+				clientid_val = t;
 			break;
 		}
 		dprintk("NFSD: purging unused client (clientid %08x)\n",
 			clp->cl_clientid.cl_id);
 		expire_client(clp);
 	}
-	if (return_val < NFSD_LAUNDROMAT_MINTIMEOUT)
-		return_val = NFSD_LAUNDROMAT_MINTIMEOUT;
+	list_for_each_safe(pos, next, &close_lru) {
+		sop = list_entry(pos, struct nfs4_stateowner, so_close_lru);
+		if (time_after((unsigned long)sop->so_time, (unsigned long)cutoff)) {
+			u = sop->so_time - cutoff;
+			if (close_val > u)
+				close_val = u;
+			break;
+		}
+		dprintk("NFSD: purging unused open stateowner (so_id %d)\n",
+			sop->so_id);
+		release_stateowner(sop);
+	}
+	if (clientid_val < NFSD_LAUNDROMAT_MINTIMEOUT)
+		clientid_val = NFSD_LAUNDROMAT_MINTIMEOUT;
 	nfs4_unlock_state();
-	return return_val;
+	return clientid_val;
 }
 
 void
@@ -1351,17 +1382,22 @@ laundromat_main(void *not_used)
 	schedule_delayed_work(&laundromat_work, t*HZ);
 }
 
-/* search ownerid_hashtbl[] for stateid owner (stateid->si_stateownerid) */
+/* search ownerid_hashtbl[] and close_lru for stateid owner
+ * (stateid->si_stateownerid)
+ */
 struct nfs4_stateowner *
-find_openstateowner_id(u32 st_id) {
+find_openstateowner_id(u32 st_id, int flags) {
 	struct list_head *pos, *next;
 	struct nfs4_stateowner *local = NULL;
-	unsigned int hashval = ownerid_hashval(st_id);
 
-	list_for_each_safe(pos, next, &ownerid_hashtbl[hashval]) {
-		local = list_entry(pos, struct nfs4_stateowner, so_idhash);
-		if(local->so_id == st_id)
-			return local;
+	dprintk("NFSD: find_openstateowner_id %d\n", st_id);
+	if (flags & CLOSE_STATE) {
+		list_for_each_safe(pos, next, &close_lru) {
+			local = list_entry(pos, struct nfs4_stateowner,
+			                    so_close_lru);
+			if(local->so_id == st_id)
+				return local;
+		}
 	}
 	return NULL;
 }
@@ -1547,11 +1583,12 @@ no_nfs4_stateid:
 	* starting by trying to look up the stateowner.
 	* If stateowner is not found - stateid is bad.
 	*/
-	if (!(sop = find_openstateowner_id(stateid->si_stateownerid))) {
+	if (!(sop = find_openstateowner_id(stateid->si_stateownerid, flags))) {
 		printk("NFSD: preprocess_seqid_op: no stateowner or nfs4_stateid!\n");
 		status = nfserr_bad_stateid;
 		goto out;
 	}
+	*sopp = sop;
 
 check_replay:
 	if (seqid == sop->so_seqid) {
@@ -1690,9 +1727,10 @@ nfsd4_close(struct svc_rqst *rqstp, stru
 			current_fh->fh_dentry->d_name.name);
 
 	nfs4_lock_state();
+	/* check close_lru for replay */
 	if ((status = nfs4_preprocess_seqid_op(current_fh, close->cl_seqid, 
 					&close->cl_stateid, 
-					CHECK_FH | OPEN_STATE, 
+					CHECK_FH | OPEN_STATE | CLOSE_STATE,
 					&close->cl_stateowner, &stp, NULL)))
 		goto out; 
 	/*
@@ -1854,6 +1892,8 @@ alloc_init_lock_stateowner(unsigned int 
 	INIT_LIST_HEAD(&sop->so_perclient);
 	INIT_LIST_HEAD(&sop->so_perfilestate);
 	INIT_LIST_HEAD(&sop->so_perlockowner);
+	INIT_LIST_HEAD(&sop->so_close_lru); /* not used */
+	sop->so_time = 0;
 	list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]);
 	list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]);
 	list_add(&sop->so_perclient, &clp->cl_perclient);
@@ -2351,6 +2391,7 @@ nfs4_state_init(void)
 	memset(&zerostateid, 0, sizeof(stateid_t));
 	memset(&onestateid, ~0, sizeof(stateid_t));
 
+	INIT_LIST_HEAD(&close_lru);
 	INIT_LIST_HEAD(&client_lru);
 	init_MUTEX(&client_sema);
 	boot_time = get_seconds();
diff -puN include/linux/nfsd/state.h~kNFSdv4-6-of-10-Keep-state-to-allow-replays-for-close-to-work. include/linux/nfsd/state.h
--- 25/include/linux/nfsd/state.h~kNFSdv4-6-of-10-Keep-state-to-allow-replays-for-close-to-work.	2004-04-07 19:39:37.857308536 -0700
+++ 25-akpm/include/linux/nfsd/state.h	2004-04-07 19:39:37.863307624 -0700
@@ -132,6 +132,9 @@ struct nfs4_replay {
 *         release a stateowner.
 *    so_perlockowner: (open) nfs4_stateid->st_perlockowner entry - used when
 *         close is called to reap associated byte-range locks
+*    so_close_lru: (open) stateowner is placed on this list instead of being
+*         reaped (when so_perfilestate is empty) to hold the last close replay.
+*         reaped by laundramat thread after lease period.
 */
 struct nfs4_stateowner {
 	struct list_head        so_idhash;   /* hash by so_id */
@@ -139,6 +142,8 @@ struct nfs4_stateowner {
 	struct list_head        so_perclient; /* nfs4_client->cl_perclient */
 	struct list_head        so_perfilestate; /* list: nfs4_stateid */
 	struct list_head        so_perlockowner; /* nfs4_stateid->st_perlockowner */
+	struct list_head	so_close_lru; /* tail queue */
+	time_t			so_time; /* time of placement on so_close_lru */
 	int			so_is_open_owner; /* 1=openowner,0=lockowner */
 	u32                     so_id;
 	struct nfs4_client *    so_client;
@@ -194,6 +199,7 @@ struct nfs4_stateid {
 #define OPEN_STATE              0x00000004
 #define LOCK_STATE              0x00000008
 #define RDWR_STATE              0x00000010
+#define CLOSE_STATE             0x00000020
 
 #define seqid_mutating_err(err)                       \
 	(((err) != nfserr_stale_clientid) &&    \

_