
 - Introduce lock_operations
 - Switch nfs client to lock_operations.
 - Introduce vfs_setlock_posix() and vfs_setlock_fcntl() which use it.
 - Use them in fcntl_[gs]etlk{,32}.
 - Use them in lockd.
 - Privatise posix_lock_file(), posix_test_file() and posix_locks_deadlock().
 - Use lock_operations in locks_remove_posix() and locks_remove_flock().
 - Fix a race in posix_test_file().
 - Tidy up locks.c include files.
 - Eliminate LOCK_USE_CLNT
 - If a l_op->lock_insert() operation is provided, no longer set those
   locks locally.  The filesystem may do that if it chooses to.  NFS only
   does this if the fs is mounted without lock support.
 - Revert local lock removing to an earlier version in locks_remove_posix().
   Should fix bugme #16
 - Allocate file_locks on the stack rather than from slab.  Eliminates
   some failure cases and paves the way for a particularly evil plan I
   have in mind.

diff -urpNX dontdiff linux-2.5.49/fs/lockd/clntproc.c linux-2.5.49-flock/fs/lockd/clntproc.c
--- linux-2.5.49/fs/lockd/clntproc.c	2002-11-17 23:29:21.000000000 -0500
+++ linux-2.5.49-flock/fs/lockd/clntproc.c	2002-11-24 04:12:23.000000000 -0500
@@ -401,12 +401,11 @@ nlmclnt_test(struct nlm_rqst *req, struc
 	return 0;
 }
 
-static
 void nlmclnt_insert_lock_callback(struct file_lock *fl)
 {
 	nlm_get_host(fl->fl_u.nfs_fl.host);
 }
-static
+
 void nlmclnt_remove_lock_callback(struct file_lock *fl)
 {
 	if (fl->fl_u.nfs_fl.host) {
@@ -462,8 +461,6 @@ nlmclnt_lock(struct nlm_rqst *req, struc
 		fl->fl_u.nfs_fl.state = host->h_state;
 		fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
 		fl->fl_u.nfs_fl.host = host;
-		fl->fl_insert = nlmclnt_insert_lock_callback;
-		fl->fl_remove = nlmclnt_remove_lock_callback;
 	}
 
 	return nlm_stat_to_errno(resp->status);
diff -urpNX dontdiff linux-2.5.49/fs/lockd/svclock.c linux-2.5.49-flock/fs/lockd/svclock.c
--- linux-2.5.49/fs/lockd/svclock.c	2002-11-17 23:29:29.000000000 -0500
+++ linux-2.5.49-flock/fs/lockd/svclock.c	2002-11-24 04:12:23.000000000 -0500
@@ -293,7 +293,6 @@ u32
 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
 			struct nlm_lock *lock, int wait, struct nlm_cookie *cookie)
 {
-	struct file_lock	*conflock;
 	struct nlm_block	*block;
 	int			error;
 
@@ -312,59 +311,57 @@ nlmsvc_lock(struct svc_rqst *rqstp, stru
 	block = nlmsvc_lookup_block(file, lock, 0);
 
 	lock->fl.fl_flags |= FL_LOCKD;
+	if (wait)
+		lock->fl.fl_flags |= FL_SLEEP;
 
-again:
-	if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) {
-		error = posix_lock_file(&file->f_file, &lock->fl);
+	error = vfs_setlock_posix(&file->f_file, &lock->fl);
+	dprintk("lockd: vfs_setlock_posix returned %d\n", -error);
+	if ((error != -EAGAIN) || !wait)
+		goto out;
 
-		if (block)
-			nlmsvc_delete_block(block, 0);
-		up(&file->f_sema);
-
-		dprintk("lockd: posix_lock_file returned %d\n", -error);
-		switch(-error) {
-		case 0:
-			return nlm_granted;
-		case EDEADLK:
-			return nlm_deadlock;
-		case EAGAIN:
-			return nlm_lck_denied;
-		default:			/* includes ENOLCK */
-			return nlm_lck_denied_nolocks;
+	/* If we don't have a block, create and initialize it. */
+	if (!block) {
+		dprintk("lockd: blocking on this lock (allocating).\n");
+		block = nlmsvc_create_block(rqstp, file, lock, cookie);
+		if (!block) {
+			error = -ENOLCK;
+			goto out;
 		}
 	}
 
-	if (!wait) {
-		up(&file->f_sema);
-		return nlm_lck_denied;
-	}
+	/* Append to list of blocked locks */
+	nlmsvc_insert_block(block, NLM_NEVER);
 
-	if (posix_locks_deadlock(&lock->fl, conflock)) {
-		up(&file->f_sema);
-		return nlm_deadlock;
+	/*
+	 * There's a race a mile wide there.  vfs_setlock_posix() adds us
+	 * to the blocked list, but we may have just slept to allocate
+	 * memory.  So check to see whether we're still blocked and issue
+	 * a wakeup if we're not.
+	 */
+	if (!lock->fl.fl_next) {
+		nlmsvc_insert_block(block, 0);
+		svc_wake_up(block->b_daemon);
 	}
+	block = NULL;
 
-	/* If we don't have a block, create and initialize it. Then
-	 * retry because we may have slept in kmalloc. */
-	if (block == NULL) {
-		dprintk("lockd: blocking on this lock (allocating).\n");
-		if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie)))
-			return nlm_lck_denied_nolocks;
-		goto again;
-	}
+ out:
+	up(&file->f_sema);
 
-	/* Append to list of blocked */
-	nlmsvc_insert_block(block, NLM_NEVER);
+	if (block)
+		nlmsvc_delete_block(block, 0);
 
-	if (list_empty(&block->b_call.a_args.lock.fl.fl_block)) {
-		/* Now add block to block list of the conflicting lock
-		   if we haven't done so. */
-		dprintk("lockd: blocking on this lock.\n");
-		posix_block_lock(conflock, &block->b_call.a_args.lock.fl);
+	if (!error) {
+		return nlm_granted;
+	} else if (error == -EDEADLK) {
+		return nlm_deadlock;
+	} else if (error == -EAGAIN) {
+		if (wait)
+			return nlm_lck_blocked;
+		else
+			return nlm_lck_denied;
+	} else {
+		return nlm_lck_denied_nolocks;
 	}
-
-	up(&file->f_sema);
-	return nlm_lck_blocked;
 }
 
 /*
@@ -374,8 +371,6 @@ u32
 nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
 				       struct nlm_lock *conflock)
 {
-	struct file_lock	*fl;
-
 	dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
 				file->f_file.f_dentry->d_inode->i_sb->s_id,
 				file->f_file.f_dentry->d_inode->i_ino,
@@ -383,13 +378,15 @@ nlmsvc_testlock(struct nlm_file *file, s
 				(long long)lock->fl.fl_start,
 				(long long)lock->fl.fl_end);
 
-	if ((fl = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
+	conflock->fl = lock->fl;	/* a struct copy */
+	vfs_testlock_posix(&file->f_file, &conflock->fl);
+	if (conflock->fl.fl_type != F_UNLCK) {
 		dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
-				fl->fl_type, (long long)fl->fl_start,
-				(long long)fl->fl_end);
+				conflock->fl.fl_type,
+				(long long)conflock->fl.fl_start,
+				(long long)conflock->fl.fl_end);
 		conflock->caller = "somehost";	/* FIXME */
 		conflock->oh.len = 0;		/* don't return OH info */
-		conflock->fl = *fl;
 		return nlm_lck_denied;
 	}
 
@@ -419,7 +416,7 @@ nlmsvc_unlock(struct nlm_file *file, str
 	nlmsvc_cancel_blocked(file, lock);
 
 	lock->fl.fl_type = F_UNLCK;
-	error = posix_lock_file(&file->f_file, &lock->fl);
+	error = vfs_setlock_posix(&file->f_file, &lock->fl);
 
 	return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
 }
@@ -490,7 +487,6 @@ nlmsvc_grant_blocked(struct nlm_block *b
 {
 	struct nlm_file		*file = block->b_file;
 	struct nlm_lock		*lock = &block->b_call.a_args.lock;
-	struct file_lock	*conflock;
 	int			error;
 
 	dprintk("lockd: grant blocked lock %p\n", block);
@@ -506,32 +502,13 @@ nlmsvc_grant_blocked(struct nlm_block *b
 	 * binding */
 	if (block->b_granted) {
 		nlm_rebind_host(block->b_host);
-		goto callback;
-	}
-
-	/* Try the lock operation again */
-	if ((conflock = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
-		/* Bummer, we blocked again */
-		dprintk("lockd: lock still blocked\n");
-		nlmsvc_insert_block(block, NLM_NEVER);
-		posix_block_lock(conflock, &lock->fl);
-		up(&file->f_sema);
-		return;
-	}
-
-	/* Alright, no conflicting lock. Now lock it for real. If the
-	 * following yields an error, this is most probably due to low
-	 * memory. Retry the lock in a few seconds.
-	 */
-	if ((error = posix_lock_file(&file->f_file, &lock->fl)) < 0) {
-		printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
-				-error, __FUNCTION__);
-		nlmsvc_insert_block(block, 10 * HZ);
-		up(&file->f_sema);
-		return;
+	} else {
+		/* Try the lock operation again */
+		error = vfs_setlock_posix(&file->f_file, &lock->fl);
+		if (error)
+			goto err;
 	}
 
-callback:
 	/* Lock was granted by VFS. */
 	dprintk("lockd: GRANTing blocked lock.\n");
 	block->b_granted = 1;
@@ -546,6 +523,18 @@ callback:
 						nlmsvc_grant_callback) < 0)
 		nlm_release_host(block->b_call.a_host);
 	up(&file->f_sema);
+	return;
+
+ err:
+	dprintk("lockd: blocked lock retry returned %d\n", error);
+	if (error = -EAGAIN) {
+		/* We actually blocked, so go back to sleep */
+		nlmsvc_insert_block(block, NLM_NEVER);
+	} else {
+		/* Some other error.  We must retry later. */
+		nlmsvc_insert_block(block, 10 * HZ);
+	}
+	up(&file->f_sema);
 }
 
 /*
diff -urpNX dontdiff linux-2.5.49/fs/lockd/svcsubs.c linux-2.5.49-flock/fs/lockd/svcsubs.c
--- linux-2.5.49/fs/lockd/svcsubs.c	2002-11-17 23:29:48.000000000 -0500
+++ linux-2.5.49-flock/fs/lockd/svcsubs.c	2002-11-24 04:12:23.000000000 -0500
@@ -176,7 +176,7 @@ again:
 			lock.fl_type  = F_UNLCK;
 			lock.fl_start = 0;
 			lock.fl_end   = OFFSET_MAX;
-			if (posix_lock_file(&file->f_file, &lock) < 0) {
+			if (vfs_setlock_posix(&file->f_file, &lock) < 0) {
 				printk("lockd: unlock failure in %s:%d\n",
 						__FILE__, __LINE__);
 				return 1;
diff -urpNX dontdiff linux-2.5.49/fs/locks.c linux-2.5.49-flock/fs/locks.c
--- linux-2.5.49/fs/locks.c	2002-11-24 03:53:14.000000000 -0500
+++ linux-2.5.49-flock/fs/locks.c	2002-11-24 04:12:23.000000000 -0500
@@ -114,16 +114,18 @@
  *  Stephen Rothwell <sfr@canb.auug.org.au>, June, 2000.
  */
 
-#include <linux/slab.h>
+#include <linux/capability.h>
 #include <linux/file.h>
-#include <linux/smp_lock.h>
+#include <linux/fs.h>
 #include <linux/init.h>
-#include <linux/capability.h>
-#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
 #include <linux/time.h>
-#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/wait.h>
 
-#include <asm/semaphore.h>
 #include <asm/uaccess.h>
 
 #define IS_POSIX(fl)	(fl->fl_flags & FL_POSIX)
@@ -187,8 +189,6 @@ void locks_init_lock(struct file_lock *f
 	fl->fl_type = 0;
 	fl->fl_start = fl->fl_end = 0;
 	fl->fl_notify = NULL;
-	fl->fl_insert = NULL;
-	fl->fl_remove = NULL;
 }
 
 /*
@@ -219,8 +219,6 @@ void locks_copy_lock(struct file_lock *n
 	new->fl_start = fl->fl_start;
 	new->fl_end = fl->fl_end;
 	new->fl_notify = fl->fl_notify;
-	new->fl_insert = fl->fl_insert;
-	new->fl_remove = fl->fl_remove;
 	new->fl_u = fl->fl_u;
 }
 
@@ -321,8 +319,6 @@ static int flock_to_posix_lock(struct fi
 	fl->fl_file = filp;
 	fl->fl_flags = FL_POSIX;
 	fl->fl_notify = NULL;
-	fl->fl_insert = NULL;
-	fl->fl_remove = NULL;
 
 	return assign_type(fl, l->l_type);
 }
@@ -361,8 +357,6 @@ static int flock64_to_posix_lock(struct 
 	fl->fl_file = filp;
 	fl->fl_flags = FL_POSIX;
 	fl->fl_notify = NULL;
-	fl->fl_insert = NULL;
-	fl->fl_remove = NULL;
 
 	switch (l->l_type) {
 	case F_RDLCK:
@@ -397,8 +391,6 @@ static int lease_alloc(struct file *filp
 	fl->fl_start = 0;
 	fl->fl_end = OFFSET_MAX;
 	fl->fl_notify = NULL;
-	fl->fl_insert = NULL;
-	fl->fl_remove = NULL;
 
 	*flp = fl;
 	return 0;
@@ -474,14 +466,17 @@ static void locks_wake_up_blocks(struct 
  */
 static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
 {
+	struct lock_operations *l_op = NULL;
 	list_add(&fl->fl_link, &file_lock_list);
 
 	/* insert into file's list */
 	fl->fl_next = *pos;
 	*pos = fl;
 
-	if (fl->fl_insert)
-		fl->fl_insert(fl);
+	if (fl->fl_file->f_op)
+		l_op = fl->fl_file->f_op->lock;
+	if (l_op && l_op->lock_insert)
+		l_op->lock_insert(fl);
 }
 
 /*
@@ -493,6 +488,7 @@ static void locks_insert_lock(struct fil
 static void locks_delete_lock(struct file_lock **thisfl_p)
 {
 	struct file_lock *fl = *thisfl_p;
+	struct lock_operations *l_op = NULL;
 
 	*thisfl_p = fl->fl_next;
 	fl->fl_next = NULL;
@@ -504,8 +500,10 @@ static void locks_delete_lock(struct fil
 		fl->fl_fasync = NULL;
 	}
 
-	if (fl->fl_remove)
-		fl->fl_remove(fl);
+	if (fl->fl_file->f_op)
+		l_op = fl->fl_file->f_op->lock;
+	if (l_op && l_op->lock_remove)
+		l_op->lock_remove(fl);
 
 	locks_wake_up_blocks(fl);
 	locks_free_lock(fl);
@@ -592,8 +590,15 @@ static int locks_block_on_timeout(struct
 	return result;
 }
 
-struct file_lock *
-posix_test_lock(struct file *filp, struct file_lock *fl)
+/**
+ * posix_test_lock - finds a conflicting lock
+ * @filp: the file to search
+ * @fl: the template to check
+ *
+ * This function overwrites @fl with any lock which overlaps it and is
+ * owned by a different task.
+ */
+static void posix_test_lock(struct file *filp, struct file_lock *fl)
 {
 	struct file_lock *cfl;
 
@@ -601,12 +606,13 @@ posix_test_lock(struct file *filp, struc
 	for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
 		if (!IS_POSIX(cfl))
 			continue;
-		if (posix_locks_conflict(cfl, fl))
-			break;
+		if (!posix_locks_conflict(cfl, fl))
+			continue;
+
+		*fl = *cfl;
+		break;
 	}
 	unlock_kernel();
-
-	return (cfl);
 }
 
 /* This function tests for deadlock condition before putting a process to
@@ -623,7 +629,7 @@ posix_test_lock(struct file *filp, struc
  * from a broken NFS client. But broken NFS clients have a lot more to
  * worry about than proper deadlock detection anyway... --okir
  */
-int posix_locks_deadlock(struct file_lock *caller_fl,
+static int posix_locks_deadlock(struct file_lock *caller_fl,
 				struct file_lock *block_fl)
 {
 	struct list_head *tmp;
@@ -651,63 +657,6 @@ next_task:
 	return 0;
 }
 
-int locks_mandatory_locked(struct inode *inode)
-{
-	fl_owner_t owner = current->files;
-	struct file_lock *fl;
-
-	/*
-	 * Search the lock list for this inode for any POSIX locks.
-	 */
-	lock_kernel();
-	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-		if (!IS_POSIX(fl))
-			continue;
-		if (fl->fl_owner != owner)
-			break;
-	}
-	unlock_kernel();
-	return fl ? -EAGAIN : 0;
-}
-
-int locks_mandatory_area(int read_write, struct inode *inode,
-			 struct file *filp, loff_t offset,
-			 size_t count)
-{
-	struct file_lock fl;
-	int error;
-
-	fl.fl_owner = current->files;
-	fl.fl_pid = current->tgid;
-	fl.fl_file = filp;
-	fl.fl_flags = FL_POSIX | FL_ACCESS | FL_SLEEP;
-	fl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
-	fl.fl_start = offset;
-	fl.fl_end = offset + count - 1;
-
-	for (;;) {
-		error = posix_lock_file(filp, &fl);
-		if (error != -EAGAIN)
-			break;
-		error = wait_event_interruptible(fl.fl_wait, !fl.fl_next);
-		if (!error) {
-			/*
-			 * If we've been sleeping someone might have
-			 * changed the permissions behind our back.
-			 */
-			if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
-				continue;
-		}
-
-		lock_kernel();
-		locks_delete_block(&fl);
-		unlock_kernel();
-		break;
-	}
-
-	return error;
-}
-
 /* Try to create a FLOCK lock on filp. We always insert new FLOCK locks
  * at the head of the list, but that's secret knowledge known only to
  * flock_lock_file and posix_lock_file.
@@ -787,7 +736,7 @@ out:
  * To all purists: Yes, I use a few goto's. Just pass on to the next function.
  */
 
-int posix_lock_file(struct file *filp, struct file_lock *request)
+static int posix_lock_file(struct file *filp, struct file_lock *request)
 {
 	struct file_lock *fl;
 	struct file_lock *new_fl, *new_fl2;
@@ -1102,6 +1051,63 @@ out:
 	return error;
 }
 
+int locks_mandatory_locked(struct inode *inode)
+{
+	fl_owner_t owner = current->files;
+	struct file_lock *fl;
+
+	/*
+	 * Search the lock list for this inode for any POSIX locks.
+	 */
+	lock_kernel();
+	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+		if (!IS_POSIX(fl))
+			continue;
+		if (fl->fl_owner != owner)
+			break;
+	}
+	unlock_kernel();
+	return fl ? -EAGAIN : 0;
+}
+
+int locks_mandatory_area(int read_write, struct inode *inode,
+			 struct file *filp, loff_t offset,
+			 size_t count)
+{
+	struct file_lock fl;
+	int error;
+
+	fl.fl_owner = current->files;
+	fl.fl_pid = current->tgid;
+	fl.fl_file = filp;
+	fl.fl_flags = FL_POSIX | FL_ACCESS | FL_SLEEP;
+	fl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
+	fl.fl_start = offset;
+	fl.fl_end = offset + count - 1;
+
+	for (;;) {
+		error = posix_lock_file(filp, &fl);
+		if (error != -EAGAIN)
+			break;
+		error = wait_event_interruptible(fl.fl_wait, !fl.fl_next);
+		if (!error) {
+			/*
+			 * If we've been sleeping someone might have
+			 * changed the permissions behind our back.
+			 */
+			if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+				continue;
+		}
+
+		lock_kernel();
+		locks_delete_block(&fl);
+		unlock_kernel();
+		break;
+	}
+
+	return error;
+}
+
 /**
  *	lease_get_mtime
  *	@inode: the inode
@@ -1327,12 +1333,95 @@ asmlinkage long sys_flock(unsigned int f
 	return error;
 }
 
+/**
+ * vfs_setlock_posix - apply a POSIX lock to a file
+ * @filp: the file to apply the lock to
+ * @fl: the lock to apply
+ *
+ * This function contains the common code to apply a lock to a file.
+ * It's called by the 32- and 64- bit versions of fcntl_setlk and lockd.
+ * If the underlying filesystem provides a locking method, we call that.
+ * Otherwise we lock locally.
+ */
+int vfs_setlock_posix(struct file *filp, struct file_lock *fl)
+{
+	struct lock_operations *l_op = NULL;
+	int (*lock_file)(struct file *, struct file_lock *) = posix_lock_file;
+	int error;
+
+	switch (fl->fl_type) {
+	case F_RDLCK:
+		if (!(filp->f_mode & FMODE_READ))
+			return -EBADF;
+		break;
+	case F_WRLCK:
+		if (!(filp->f_mode & FMODE_WRITE))
+			return -EBADF;
+		break;
+	case F_UNLCK:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	error = security_ops->file_lock(filp, fl->fl_type);
+	if (error)
+		return error;
+
+	if (filp->f_op)
+		l_op = filp->f_op->lock;
+	if (l_op && l_op->set_lock)
+		lock_file = l_op->set_lock;
+
+	for (;;) {
+		error = lock_file(filp, fl);
+		if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP))
+			break;
+		if (fl->fl_flags & FL_LOCKD)
+			break;
+		error = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
+		if (!error)
+			continue;
+
+		lock_kernel();
+		locks_delete_block(fl);
+		unlock_kernel();
+		break;
+	}
+
+	return error;
+}
+
+/**
+ * vfs_testlock_posix - Report any conflicting lock
+ * @filp: the file to examine
+ * @fl: The lock to report any conflicts against
+ *
+ * This function contains the common code to test for conflicting locks.
+ * It's called by the 32- and 64- bit versions of fcntl getlk and by lockd.
+ * Note that this is an inherently racy interface and the only reason to use
+ * it is to implement the user interface.  In-kernel users should attempt to
+ * apply the lock and deal with the failure.
+ */
+void vfs_testlock_posix(struct file *filp, struct file_lock *fl)
+{
+	struct lock_operations *l_op = NULL;
+
+	if (filp->f_op)
+		l_op = filp->f_op->lock;
+	if (l_op && l_op->get_lock) {
+		l_op->get_lock(filp, fl);
+	} else {
+		posix_test_lock(filp, fl);
+	}
+}
+
 /* Report the first existing lock that would conflict with l.
  * This implements the F_GETLK command of fcntl().
  */
 int fcntl_getlk(struct file *filp, struct flock *l)
 {
-	struct file_lock *fl, file_lock;
+	struct file_lock file_lock;
 	struct flock flock;
 	int error;
 
@@ -1343,49 +1432,38 @@ int fcntl_getlk(struct file *filp, struc
 	if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
 		goto out;
 
+	locks_init_lock(&file_lock);
 	error = flock_to_posix_lock(filp, &file_lock, &flock);
 	if (error)
 		goto out;
 
-	if (filp->f_op && filp->f_op->lock) {
-		error = filp->f_op->lock(filp, F_GETLK, &file_lock);
-		if (error < 0)
-			goto out;
-		else if (error == LOCK_USE_CLNT)
-		  /* Bypass for NFS with no locking - 2.0.36 compat */
-		  fl = posix_test_lock(filp, &file_lock);
-		else
-		  fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
-	} else {
-		fl = posix_test_lock(filp, &file_lock);
-	}
+	vfs_testlock_posix(filp, &file_lock);
  
-	flock.l_type = F_UNLCK;
-	if (fl != NULL) {
-		flock.l_pid = fl->fl_pid;
+	flock.l_type = file_lock.fl_type;
+	if (flock.l_type != F_UNLCK) {
 #if BITS_PER_LONG == 32
 		/*
 		 * Make sure we can represent the posix lock via
 		 * legacy 32bit flock.
 		 */
 		error = -EOVERFLOW;
-		if (fl->fl_start > OFFT_OFFSET_MAX)
+		if (file_lock.fl_start > OFFT_OFFSET_MAX)
 			goto out;
-		if ((fl->fl_end != OFFSET_MAX)
-		    && (fl->fl_end > OFFT_OFFSET_MAX))
+		if ((file_lock.fl_end != OFFSET_MAX)
+		    && (file_lock.fl_end > OFFT_OFFSET_MAX))
 			goto out;
 #endif
-		flock.l_start = fl->fl_start;
-		flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
-			fl->fl_end - fl->fl_start + 1;
+		flock.l_pid = file_lock.fl_pid;
+		flock.l_start = file_lock.fl_start;
+		flock.l_len = file_lock.fl_end == OFFSET_MAX ? 0 :
+			file_lock.fl_end - file_lock.fl_start + 1;
 		flock.l_whence = 0;
-		flock.l_type = fl->fl_type;
 	}
 	error = -EFAULT;
 	if (!copy_to_user(l, &flock, sizeof(flock)))
 		error = 0;
   
-out:
+ out:
 	return error;
 }
 
@@ -1394,14 +1472,11 @@ out:
  */
 int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock *l)
 {
-	struct file_lock *file_lock = locks_alloc_lock(0);
+	struct file_lock file_lock;
 	struct flock flock;
 	struct inode *inode;
 	int error;
 
-	if (file_lock == NULL)
-		return -ENOLCK;
-
 	/*
 	 * This might block, so we do it before checking the inode.
 	 */
@@ -1411,7 +1486,6 @@ int fcntl_setlk(struct file *filp, unsig
 
 	inode = filp->f_dentry->d_inode;
 
-#ifdef CONFIG_MMU
 	/* Don't allow mandatory locks on files that may be memory mapped
 	 * and shared.
 	 */
@@ -1424,59 +1498,18 @@ int fcntl_setlk(struct file *filp, unsig
 			goto out;
 		}
 	}
-#endif
 
-	error = flock_to_posix_lock(filp, file_lock, &flock);
+	locks_init_lock(&file_lock);
+	error = flock_to_posix_lock(filp, &file_lock, &flock);
 	if (error)
 		goto out;
 	if (cmd == F_SETLKW) {
-		file_lock->fl_flags |= FL_SLEEP;
+		file_lock.fl_flags |= FL_SLEEP;
 	}
 	
-	error = -EBADF;
-	switch (flock.l_type) {
-	case F_RDLCK:
-		if (!(filp->f_mode & FMODE_READ))
-			goto out;
-		break;
-	case F_WRLCK:
-		if (!(filp->f_mode & FMODE_WRITE))
-			goto out;
-		break;
-	case F_UNLCK:
-		break;
-	default:
-		error = -EINVAL;
-		goto out;
-	}
-
-	error = security_ops->file_lock(filp, file_lock->fl_type);
-	if (error)
-		goto out;
-
-	if (filp->f_op && filp->f_op->lock != NULL) {
-		error = filp->f_op->lock(filp, cmd, file_lock);
-		if (error < 0)
-			goto out;
-	}
-
-	for (;;) {
-		error = posix_lock_file(filp, file_lock);
-		if ((error != -EAGAIN) || (cmd == F_SETLK))
-			break;
-		error = wait_event_interruptible(file_lock->fl_wait,
-				!file_lock->fl_next);
-		if (!error)
-			continue;
-
-		lock_kernel();
-		locks_delete_block(file_lock);
-		unlock_kernel();
-		break;
-	}
+	error = vfs_setlock_posix(filp, &file_lock);
 
  out:
-	locks_free_lock(file_lock);
 	return error;
 }
 
@@ -1486,7 +1519,7 @@ int fcntl_setlk(struct file *filp, unsig
  */
 int fcntl_getlk64(struct file *filp, struct flock64 *l)
 {
-	struct file_lock *fl, file_lock;
+	struct file_lock file_lock;
 	struct flock64 flock;
 	int error;
 
@@ -1497,31 +1530,20 @@ int fcntl_getlk64(struct file *filp, str
 	if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
 		goto out;
 
+	locks_init_lock(&file_lock);
 	error = flock64_to_posix_lock(filp, &file_lock, &flock);
 	if (error)
 		goto out;
 
-	if (filp->f_op && filp->f_op->lock) {
-		error = filp->f_op->lock(filp, F_GETLK, &file_lock);
-		if (error < 0)
-			goto out;
-		else if (error == LOCK_USE_CLNT)
-		  /* Bypass for NFS with no locking - 2.0.36 compat */
-		  fl = posix_test_lock(filp, &file_lock);
-		else
-		  fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
-	} else {
-		fl = posix_test_lock(filp, &file_lock);
-	}
+	vfs_testlock_posix(filp, &file_lock);
  
-	flock.l_type = F_UNLCK;
-	if (fl != NULL) {
-		flock.l_pid = fl->fl_pid;
-		flock.l_start = fl->fl_start;
-		flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
-			fl->fl_end - fl->fl_start + 1;
+	flock.l_type = file_lock.fl_type;
+	if (flock.l_type != F_UNLCK) {
+		flock.l_pid = file_lock.fl_pid;
+		flock.l_start = file_lock.fl_start;
+		flock.l_len = file_lock.fl_end == OFFSET_MAX ? 0 :
+			file_lock.fl_end - file_lock.fl_start + 1;
 		flock.l_whence = 0;
-		flock.l_type = fl->fl_type;
 	}
 	error = -EFAULT;
 	if (!copy_to_user(l, &flock, sizeof(flock)))
@@ -1536,14 +1558,11 @@ out:
  */
 int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 *l)
 {
-	struct file_lock *file_lock = locks_alloc_lock(0);
+	struct file_lock file_lock;
 	struct flock64 flock;
 	struct inode *inode;
 	int error;
 
-	if (file_lock == NULL)
-		return -ENOLCK;
-
 	/*
 	 * This might block, so we do it before checking the inode.
 	 */
@@ -1566,57 +1585,17 @@ int fcntl_setlk64(struct file *filp, uns
 		}
 	}
 
-	error = flock64_to_posix_lock(filp, file_lock, &flock);
+	locks_init_lock(&file_lock);
+	error = flock64_to_posix_lock(filp, &file_lock, &flock);
 	if (error)
 		goto out;
 	if (cmd == F_SETLKW64) {
-		file_lock->fl_flags |= FL_SLEEP;
-	}
-	
-	error = -EBADF;
-	switch (flock.l_type) {
-	case F_RDLCK:
-		if (!(filp->f_mode & FMODE_READ))
-			goto out;
-		break;
-	case F_WRLCK:
-		if (!(filp->f_mode & FMODE_WRITE))
-			goto out;
-		break;
-	case F_UNLCK:
-		break;
-	default:
-		error = -EINVAL;
-		goto out;
-	}
-
-	error = security_ops->file_lock(filp, file_lock->fl_type);
-	if (error)
-		goto out;
-
-	if (filp->f_op && filp->f_op->lock != NULL) {
-		error = filp->f_op->lock(filp, cmd, file_lock);
-		if (error < 0)
-			goto out;
+		file_lock.fl_flags |= FL_SLEEP;
 	}
 
-	for (;;) {
-		error = posix_lock_file(filp, file_lock);
-		if ((error != -EAGAIN) || (cmd == F_SETLK64))
-			break;
-		error = wait_event_interruptible(file_lock->fl_wait,
-				!file_lock->fl_next);
-		if (!error)
-			continue;
-
-		lock_kernel();
-		locks_delete_block(file_lock);
-		unlock_kernel();
-		break;
-	}
+	error = vfs_setlock_posix(filp, &file_lock);
 
-out:
-	locks_free_lock(file_lock);
+ out:
 	return error;
 }
 #endif /* BITS_PER_LONG == 32 */
@@ -1628,30 +1607,34 @@ out:
  */
 void locks_remove_posix(struct file *filp, fl_owner_t owner)
 {
-	struct file_lock lock;
+	struct file_lock *fl, **before;
+	struct lock_operations *l_op = NULL;
+	struct inode *inode = filp->f_dentry->d_inode;
 
 	/*
-	 * If there are no locks held on this file, we don't need to call
-	 * posix_lock_file().  Another process could be setting a lock on this
-	 * file at the same time, but we wouldn't remove that lock anyway.
+	 * If there are no locks held on this file, we don't need to do
+	 * anything.  Another thread could be setting a lock on this
+	 * file at the same time, but it's a race we just won.
 	 */
-	if (!filp->f_dentry->d_inode->i_flock)
+	if (!inode->i_flock)
 		return;
 
-	lock.fl_type = F_UNLCK;
-	lock.fl_flags = FL_POSIX;
-	lock.fl_start = 0;
-	lock.fl_end = OFFSET_MAX;
-	lock.fl_owner = owner;
-	lock.fl_pid = current->tgid;
-	lock.fl_file = filp;
-
-	if (filp->f_op && filp->f_op->lock != NULL) {
-		filp->f_op->lock(filp, F_SETLK, &lock);
-		/* Ignore any error -- we must remove the locks anyway */
+	if (filp->f_op)
+		l_op = filp->f_op->lock;
+	if (l_op && l_op->remove_posix) {
+		l_op->remove_posix(filp, owner);
+	} else {
+		lock_kernel();
+		before = &inode->i_flock;
+		while ((fl = *before) != NULL) {
+			if (IS_POSIX(fl) && fl->fl_owner == owner) {
+				locks_delete_lock(before);
+				continue;
+			}
+			before = &fl->fl_next;
+		}
+		unlock_kernel();
 	}
-
-	posix_lock_file(filp, &lock);
 }
 
 /*
diff -urpNX dontdiff linux-2.5.49/fs/nfs/file.c linux-2.5.49-flock/fs/nfs/file.c
--- linux-2.5.49/fs/nfs/file.c	2002-11-17 23:29:49.000000000 -0500
+++ linux-2.5.49-flock/fs/nfs/file.c	2002-11-24 04:12:23.000000000 -0500
@@ -40,20 +40,6 @@ static ssize_t nfs_file_write(struct kio
 static int  nfs_file_flush(struct file *);
 static int  nfs_fsync(struct file *, struct dentry *dentry, int datasync);
 
-struct file_operations nfs_file_operations = {
-	.llseek		= remote_llseek,
-	.read		= do_sync_read,
-	.write		= do_sync_write,
-	.aio_read		= nfs_file_read,
-	.aio_write		= nfs_file_write,
-	.mmap		= nfs_file_mmap,
-	.open		= nfs_open,
-	.flush		= nfs_file_flush,
-	.release	= nfs_release,
-	.fsync		= nfs_fsync,
-	.lock		= nfs_lock,
-};
-
 struct inode_operations nfs_file_inode_operations = {
 	.permission	= nfs_permission,
 	.getattr	= nfs_getattr,
@@ -239,10 +225,13 @@ nfs_lock(struct file *filp, int cmd, str
 	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
 		return -ENOLCK;
 
-	/* Fake OK code if mounted without NLM support */
+	/* lock locally if mounted without NLM support */
 	if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) {
-		if (IS_GETLK(cmd))
-			status = LOCK_USE_CLNT;
+		if (IS_GETLK(cmd)) {
+			vfs_testlock_posix(filp, fl);
+		} else {
+			status = vfs_setlock_posix(filp, fl);
+		}
 		goto out_ok;
 	}
 
@@ -295,3 +284,52 @@ nfs_lock(struct file *filp, int cmd, str
 	}
 	return status;
 }
+
+static int nfs_set_lock(struct file *filp, struct file_lock *fl)
+{
+	if (fl->fl_flags & FL_SLEEP) {
+		return nfs_lock(filp, F_SETLKW, fl);
+	} else {
+		return nfs_lock(filp, F_SETLK, fl);
+	}
+}
+
+static void nfs_get_lock(struct file *filp, struct file_lock *fl)
+{
+	int status = nfs_lock(filp, F_GETLK, fl);
+	if (status) {
+		fl->fl_type = F_UNLCK;
+	}
+}
+
+#if 0
+static int nfs_remove_posix(struct file *filp, fl_owner_t id)
+{
+	printk("remove posix support not yet implemented\n");
+}
+#endif
+
+extern void nlmclnt_insert_lock_callback(struct file_lock *fl);
+extern void nlmclnt_remove_lock_callback(struct file_lock *fl);
+
+static struct lock_operations nfs_client_lops = {
+	.set_lock =	nfs_set_lock,
+	.get_lock =	nfs_get_lock,
+/*	.remove_posix =	nfs_remove_posix, */
+	.lock_insert =	nlmclnt_insert_lock_callback,
+	.lock_remove =	nlmclnt_remove_lock_callback,
+};
+
+struct file_operations nfs_file_operations = {
+	.llseek		= remote_llseek,
+	.read		= do_sync_read,
+	.write		= do_sync_write,
+	.aio_read	= nfs_file_read,
+	.aio_write	= nfs_file_write,
+	.mmap		= nfs_file_mmap,
+	.open		= nfs_open,
+	.flush		= nfs_file_flush,
+	.release	= nfs_release,
+	.fsync		= nfs_fsync,
+	.lock		= &nfs_client_lops,
+};
diff -urpNX dontdiff linux-2.5.49/include/linux/fs.h linux-2.5.49-flock/include/linux/fs.h
--- linux-2.5.49/include/linux/fs.h	2002-11-24 03:53:15.000000000 -0500
+++ linux-2.5.49-flock/include/linux/fs.h	2002-11-24 04:12:23.000000000 -0500
@@ -536,8 +536,6 @@ struct file_lock {
 	loff_t fl_end;
 
 	void (*fl_notify)(struct file_lock *);	/* unblock callback */
-	void (*fl_insert)(struct file_lock *);	/* lock insertion callback */
-	void (*fl_remove)(struct file_lock *);	/* lock removal callback */
 
 	struct fasync_struct *	fl_fasync; /* for lease break notifications */
 	unsigned long fl_break_time;	/* for nonblocking lease breaks */
@@ -567,13 +565,12 @@ extern int fcntl_setlk64(struct file *, 
 /* fs/locks.c */
 extern void locks_init_lock(struct file_lock *);
 extern void locks_copy_lock(struct file_lock *, struct file_lock *);
+extern int vfs_setlock_posix(struct file *, struct file_lock *);
+extern void vfs_testlock_posix(struct file *, struct file_lock *);
 extern void locks_remove_posix(struct file *, fl_owner_t);
 extern void locks_remove_flock(struct file *);
-extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
-extern int posix_lock_file(struct file *, struct file_lock *);
 extern void posix_block_lock(struct file_lock *, struct file_lock *);
 extern void posix_unblock_lock(struct file *, struct file_lock *);
-extern int posix_locks_deadlock(struct file_lock *, struct file_lock *);
 extern int __break_lease(struct inode *inode, unsigned int flags);
 extern void lease_get_mtime(struct inode *, struct timespec *time);
 extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
@@ -730,6 +727,42 @@ typedef struct {
 
 typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, unsigned long);
 
+/**
+ * struct lock_operations - filesystem hooks for file locking
+ *
+ * This struct is a work in progress.  It is intended to be per-filesystem;
+ * indeed it could be part of f_ops were it not pure bloat for non-network
+ * filesystems.  I suspect lock_insert and lock_remove are now unnecessary,
+ * but need feedback from FS maintainers.
+ * 
+ * @set_lock:
+ *	Attempt to set a new lock.  BKL not held, may sleep.
+ * @get_lock:
+ *	Return any lock which would conflict with the incoming lock.
+ *	No locks held, may sleep.
+ * @remove_posix:
+ *	A process closed a file descriptor.  Any locks on this @filp owned
+ *	by @owner should be removed.  BKL not held, may sleep.
+ * @remove_flock:
+ *	This @filp has been closed.  All locks owned by this process should
+ *	be removed.  BKL not held, may sleep.
+ * @lock_insert:
+ *	Notification that @fl, which was previously blocked, is now being
+ *	inserted.  BKL might not be held.  Must not sleep.
+ * @lock_remove:
+ *	Notification that @fl, which was an active lock, is now being
+ *	removed from the @filp.  BKL might not be held.  Must not sleep.
+ */
+
+struct lock_operations {
+	int (*set_lock) (struct file *filp, struct file_lock *fl);
+	void (*get_lock) (struct file *filp, struct file_lock *fl);
+	void (*remove_posix) (struct file *filp, fl_owner_t owner);
+	void (*remove_flock) (struct file *filp);
+	void (*lock_insert) (struct file_lock *fl);
+	void (*lock_remove) (struct file_lock *fl);
+};
+
 /*
  * NOTE:
  * read, write, poll, fsync, readv, writev can be called
@@ -753,7 +786,7 @@ struct file_operations {
 	int (*fsync) (struct file *, struct dentry *, int datasync);
 	int (*aio_fsync) (struct kiocb *, int datasync);
 	int (*fasync) (int, struct file *, int);
-	int (*lock) (struct file *, int, struct file_lock *);
+	struct lock_operations *lock;
 	ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
 	ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
 	ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
@@ -1000,11 +1033,6 @@ extern long do_mount(char *, char *, cha
 
 extern int vfs_statfs(struct super_block *, struct statfs *);
 
-/* Return value for VFS lock functions - tells locks.c to lock conventionally
- * REALLY kosha for root NFS and nfs_lock
- */ 
-#define LOCK_USE_CLNT 1
-
 #define FLOCK_VERIFY_READ  1
 #define FLOCK_VERIFY_WRITE 2
 
diff -urpNX dontdiff linux-2.5.49/kernel/ksyms.c linux-2.5.49-flock/kernel/ksyms.c
--- linux-2.5.49/kernel/ksyms.c	2002-11-24 03:53:15.000000000 -0500
+++ linux-2.5.49-flock/kernel/ksyms.c	2002-11-24 04:12:23.000000000 -0500
@@ -229,11 +229,10 @@ EXPORT_SYMBOL(generic_ro_fops);
 EXPORT_SYMBOL(file_lock_list);
 EXPORT_SYMBOL(locks_init_lock);
 EXPORT_SYMBOL(locks_copy_lock);
-EXPORT_SYMBOL(posix_lock_file);
-EXPORT_SYMBOL(posix_test_lock);
+EXPORT_SYMBOL(vfs_setlock_posix);
+EXPORT_SYMBOL(vfs_testlock_posix);
 EXPORT_SYMBOL(posix_block_lock);
 EXPORT_SYMBOL(posix_unblock_lock);
-EXPORT_SYMBOL(posix_locks_deadlock);
 EXPORT_SYMBOL(locks_mandatory_area);
 EXPORT_SYMBOL(dput);
 EXPORT_SYMBOL(have_submounts);
