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



---

 fs/nfsd/nfs4proc.c        |    4 ++
 fs/nfsd/nfs4state.c       |   79 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/nfsd/nfs4xdr.c         |   19 +++++++++++
 include/linux/nfs4.h      |    1 
 include/linux/nfsd/nfsd.h |    1 
 include/linux/nfsd/xdr4.h |    8 ++++
 6 files changed, 112 insertions(+)

diff -puN fs/nfsd/nfs4proc.c~knfsd-implement-RELEASE_LOCKOWNER fs/nfsd/nfs4proc.c
--- 25/fs/nfsd/nfs4proc.c~knfsd-implement-RELEASE_LOCKOWNER	2004-02-25 02:32:20.000000000 -0800
+++ 25-akpm/fs/nfsd/nfs4proc.c	2004-02-25 02:32:20.000000000 -0800
@@ -728,6 +728,7 @@ nfsd4_proc_compound(struct svc_rqst *rqs
 		   (op->opnum == OP_SETCLIENTID) ||
 		   (op->opnum == OP_SETCLIENTID_CONFIRM) ||
 		   (op->opnum == OP_RENEW) || (op->opnum == OP_RESTOREFH) ||
+		   (op->opnum == OP_RELEASE_LOCKOWNER) ||
 		   (op->opnum == OP_SETATTR))) {
 			op->status = nfserr_nofilehandle;
 			goto encode_op;
@@ -834,6 +835,9 @@ nfsd4_proc_compound(struct svc_rqst *rqs
 		case OP_WRITE:
 			op->status = nfsd4_write(rqstp, &current_fh, &op->u.write);
 			break;
+		case OP_RELEASE_LOCKOWNER:
+			op->status = nfsd4_release_lockowner(rqstp, &op->u.release_lockowner);
+			break;
 		default:
 			BUG_ON(op->status == nfs_ok);
 			break;
diff -puN fs/nfsd/nfs4state.c~knfsd-implement-RELEASE_LOCKOWNER fs/nfsd/nfs4state.c
--- 25/fs/nfsd/nfs4state.c~knfsd-implement-RELEASE_LOCKOWNER	2004-02-25 02:32:20.000000000 -0800
+++ 25-akpm/fs/nfsd/nfs4state.c	2004-02-25 02:32:20.000000000 -0800
@@ -43,6 +43,7 @@
 #include <linux/nfsd/cache.h>
 #include <linux/mount.h>
 #include <linux/workqueue.h>
+#include <linux/smp_lock.h>
 #include <linux/nfs4.h>
 #include <linux/nfsd/state.h>
 #include <linux/nfsd/xdr4.h>
@@ -2138,6 +2139,84 @@ out_nfserr:
 	goto out;
 }
 
+/*
+ * returns
+ * 	1: locks held by lockowner
+ * 	0: no locks held by lockowner
+ */
+static int
+check_for_locks(struct file *filp, struct nfs4_stateowner *lowner)
+{
+	struct file_lock **flpp;
+	struct inode *inode = filp->f_dentry->d_inode;
+	int status = 0;
+
+	lock_kernel();
+	for (flpp = &inode->i_flock; *flpp != NULL; flpp = &(*flpp)->fl_next) {
+		if ((*flpp)->fl_owner == (fl_owner_t)lowner)
+			status = 1;
+			goto out;
+	}
+out:
+	unlock_kernel();
+	return status;
+}
+
+int
+nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *rlockowner)
+{
+	clientid_t *clid = &rlockowner->rl_clientid;
+	struct list_head *pos, *next;
+	struct nfs4_stateowner *local = NULL;
+	struct xdr_netobj *owner = &rlockowner->rl_owner;
+	int status, i;
+
+	dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
+		clid->cl_boot, clid->cl_id);
+
+	/* XXX check for lease expiration */
+
+	status = nfserr_stale_clientid;
+	if (STALE_CLIENTID(clid)) {
+		printk("NFSD: nfsd4_release_lockowner: clientid is stale!\n");
+		return status;
+	}
+
+	nfs4_lock_state();
+
+	/* find the lockowner */
+        status = nfs_ok;
+	for (i=0; i < LOCK_HASH_SIZE; i++) {
+		list_for_each_safe(pos, next, &lock_ownerstr_hashtbl[i]) {
+			local = list_entry(pos, struct nfs4_stateowner,
+						so_strhash);
+			if(cmp_owner_str(local, owner, clid))
+				break;
+		}
+	}
+	if (local) {
+		struct nfs4_stateid *stp;
+
+		/* check for any locks held by any stateid associated with the
+		 * (lock) stateowner */
+		status = nfserr_locks_held;
+		list_for_each_safe(pos, next, &local->so_perfilestate) {
+			stp = list_entry(pos, struct nfs4_stateid,
+					                    st_perfilestate);
+			if(stp->st_vfs_set) {
+				if (check_for_locks(&stp->st_vfs_file, local))
+					goto out;
+			}
+		}
+		/* no locks held by (lock) stateowner */
+		status = nfs_ok;
+		release_stateowner(local);
+	}
+out:
+	nfs4_unlock_state();
+	return status;
+}
+
 /* 
  * Start and stop routines
  */
diff -puN fs/nfsd/nfs4xdr.c~knfsd-implement-RELEASE_LOCKOWNER fs/nfsd/nfs4xdr.c
--- 25/fs/nfsd/nfs4xdr.c~knfsd-implement-RELEASE_LOCKOWNER	2004-02-25 02:32:20.000000000 -0800
+++ 25-akpm/fs/nfsd/nfs4xdr.c	2004-02-25 02:32:20.000000000 -0800
@@ -993,6 +993,20 @@ nfsd4_decode_write(struct nfsd4_compound
 }
 
 static int
+nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner)
+{
+	DECODE_HEAD;
+
+	READ_BUF(12);
+	COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));
+	READ32(rlockowner->rl_owner.len);
+	READ_BUF(rlockowner->rl_owner.len);
+	READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len);
+
+	DECODE_TAIL;
+}
+
+static int
 nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
 {
 	DECODE_HEAD;
@@ -1157,6 +1171,9 @@ nfsd4_decode_compound(struct nfsd4_compo
 		case OP_WRITE:
 			op->status = nfsd4_decode_write(argp, &op->u.write);
 			break;
+		case OP_RELEASE_LOCKOWNER:
+			op->status = nfsd4_decode_release_lockowner(argp, &op->u.release_lockowner);
+			break;
 		default:
 			/*
 			 * According to spec, anything greater than OP_WRITE
@@ -2349,6 +2366,8 @@ nfsd4_encode_operation(struct nfsd4_comp
 	case OP_WRITE:
 		nfsd4_encode_write(resp, op->status, &op->u.write);
 		break;
+	case OP_RELEASE_LOCKOWNER:
+		break;
 	default:
 		break;
 	}
diff -puN include/linux/nfs4.h~knfsd-implement-RELEASE_LOCKOWNER include/linux/nfs4.h
--- 25/include/linux/nfs4.h~knfsd-implement-RELEASE_LOCKOWNER	2004-02-25 02:32:20.000000000 -0800
+++ 25-akpm/include/linux/nfs4.h	2004-02-25 02:32:20.000000000 -0800
@@ -86,6 +86,7 @@ enum nfs_opnum4 {
 	OP_SETCLIENTID_CONFIRM = 36,
 	OP_VERIFY = 37,
 	OP_WRITE = 38,
+	OP_RELEASE_LOCKOWNER = 39,
 };
 
 enum nfsstat4 {
diff -puN include/linux/nfsd/nfsd.h~knfsd-implement-RELEASE_LOCKOWNER include/linux/nfsd/nfsd.h
--- 25/include/linux/nfsd/nfsd.h~knfsd-implement-RELEASE_LOCKOWNER	2004-02-25 02:32:20.000000000 -0800
+++ 25-akpm/include/linux/nfsd/nfsd.h	2004-02-25 02:32:20.000000000 -0800
@@ -194,6 +194,7 @@ void		nfsd_lockd_shutdown(void);
 #define	nfserr_attrnotsupp	__constant_htonl(NFSERR_ATTRNOTSUPP)
 #define	nfserr_bad_xdr		__constant_htonl(NFSERR_BAD_XDR)
 #define	nfserr_openmode		__constant_htonl(NFSERR_OPENMODE)
+#define	nfserr_locks_held	__constant_htonl(NFSERR_LOCKS_HELD)
 
 /* error codes for internal use */
 /* if a request fails due to kmalloc failure, it gets dropped.
diff -puN include/linux/nfsd/xdr4.h~knfsd-implement-RELEASE_LOCKOWNER include/linux/nfsd/xdr4.h
--- 25/include/linux/nfsd/xdr4.h~knfsd-implement-RELEASE_LOCKOWNER	2004-02-25 02:32:20.000000000 -0800
+++ 25-akpm/include/linux/nfsd/xdr4.h	2004-02-25 02:32:20.000000000 -0800
@@ -263,6 +263,10 @@ struct nfsd4_readdir {
 	u32 *			offset;
 };
 
+struct nfsd4_release_lockowner {
+	clientid_t        rl_clientid;
+	struct xdr_netobj rl_owner;
+};
 struct nfsd4_readlink {
 	struct svc_rqst *rl_rqstp;          /* request */
 	struct svc_fh *	rl_fhp;             /* request */
@@ -359,6 +363,7 @@ struct nfsd4_op {
 		struct nfsd4_setclientid_confirm setclientid_confirm;
 		struct nfsd4_verify		verify;
 		struct nfsd4_write		write;
+		struct nfsd4_release_lockowner	release_lockowner;
 	} u;
 	struct nfs4_replay *			replay;
 };
@@ -441,6 +446,9 @@ extern int nfsd4_lockt(struct svc_rqst *
 		struct nfsd4_lockt *lockt);
 extern int nfsd4_locku(struct svc_rqst *rqstp, struct svc_fh *current_fh, 
 		struct nfsd4_locku *locku);
+extern int
+nfsd4_release_lockowner(struct svc_rqst *rqstp,
+		struct nfsd4_release_lockowner *rlockowner);
 #endif
 
 /*

_