diff -urNX dontdiff linux-2.5.15/fs/fcntl.c linux-2.5.15-flock/fs/fcntl.c
--- linux-2.5.15/fs/fcntl.c	Thu May  9 16:21:55 2002
+++ linux-2.5.15-flock/fs/fcntl.c	Mon May 13 07:46:07 2002
@@ -300,11 +300,11 @@
 			unlock_kernel();
 			break;
 		case F_GETLK:
-			err = fcntl_getlk(fd, (struct flock *) arg);
+			err = fcntl_getlk(filp, (struct flock *) arg);
 			break;
 		case F_SETLK:
 		case F_SETLKW:
-			err = fcntl_setlk(fd, cmd, (struct flock *) arg);
+			err = fcntl_setlk(filp, cmd, (struct flock *) arg);
 			break;
 		case F_GETOWN:
 			/*
@@ -386,13 +386,13 @@
 
 	switch (cmd) {
 		case F_GETLK64:
-			err = fcntl_getlk64(fd, (struct flock64 *) arg);
+			err = fcntl_getlk64(filp, (struct flock64 *) arg);
 			break;
 		case F_SETLK64:
-			err = fcntl_setlk64(fd, cmd, (struct flock64 *) arg);
+			err = fcntl_setlk64(filp, cmd, (struct flock64 *) arg);
 			break;
 		case F_SETLKW64:
-			err = fcntl_setlk64(fd, cmd, (struct flock64 *) arg);
+			err = fcntl_setlk64(filp, cmd, (struct flock64 *) arg);
 			break;
 		default:
 			err = do_fcntl(fd, cmd, arg, filp);
diff -urNX dontdiff linux-2.5.15/fs/lockd/svclock.c linux-2.5.15-flock/fs/lockd/svclock.c
--- linux-2.5.15/fs/lockd/svclock.c	Thu May  9 16:23:11 2002
+++ linux-2.5.15-flock/fs/lockd/svclock.c	Thu May 16 14:55:01 2002
@@ -242,7 +242,7 @@
 	if (unlock && block->b_granted) {
 		dprintk("lockd: deleting granted lock\n");
 		fl->fl_type = F_UNLCK;
-		posix_lock_file(&block->b_file->f_file, fl, 0);
+		posix_lock_file(&block->b_file->f_file, fl);
 		block->b_granted = 0;
 	} else {
 		dprintk("lockd: unblocking blocked lock\n");
@@ -324,7 +324,7 @@
 
 again:
 	if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) {
-		error = posix_lock_file(&file->f_file, &lock->fl, 0);
+		error = posix_lock_file(&file->f_file, &lock->fl);
 
 		if (block)
 			nlmsvc_delete_block(block, 0);
@@ -428,7 +428,7 @@
 	nlmsvc_cancel_blocked(file, lock);
 
 	lock->fl.fl_type = F_UNLCK;
-	error = posix_lock_file(&file->f_file, &lock->fl, 0);
+	error = posix_lock_file(&file->f_file, &lock->fl);
 
 	return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
 }
@@ -533,7 +533,7 @@
 	 * 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)) < 0) {
+	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);
diff -urNX dontdiff linux-2.5.15/fs/lockd/svcsubs.c linux-2.5.15-flock/fs/lockd/svcsubs.c
--- linux-2.5.15/fs/lockd/svcsubs.c	Thu May  9 16:21:50 2002
+++ linux-2.5.15-flock/fs/lockd/svcsubs.c	Thu May 16 14:55:30 2002
@@ -176,7 +176,7 @@
 			lock.fl_type  = F_UNLCK;
 			lock.fl_start = 0;
 			lock.fl_end   = OFFSET_MAX;
-			if (posix_lock_file(&file->f_file, &lock, 0) < 0) {
+			if (posix_lock_file(&file->f_file, &lock) < 0) {
 				printk("lockd: unlock failure in %s:%d\n",
 						__FILE__, __LINE__);
 				return 1;
diff -urNX dontdiff linux-2.5.15/fs/locks.c linux-2.5.15-flock/fs/locks.c
--- linux-2.5.15/fs/locks.c	Thu May  9 16:23:59 2002
+++ linux-2.5.15-flock/fs/locks.c	Wed May 22 01:13:56 2002
@@ -1,4 +1,3 @@
-#define MSNFS	/* HACK HACK */
 /*
  *  linux/fs/locks.c
  *
@@ -115,6 +114,11 @@
  *  Stephen Rothwell <sfr@canb.auug.org.au>, June, 2000.
  */
 
+/* Each inode's list of locks first has 0 or more leases,
+ * then 0 or more whole-file locks (unsorted),
+ * then 0 or more range locks (sorted first on owner, then on location in file)
+ */
+
 #include <linux/slab.h>
 #include <linux/file.h>
 #include <linux/smp_lock.h>
@@ -129,11 +133,20 @@
 int leases_enable = 1;
 int lease_break_time = 45;
 
+#define for_each_lock(inode, lockp) \
+	for (lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next)
+
 LIST_HEAD(file_lock_list);
 static LIST_HEAD(blocked_list);
 
+static spinlock_t file_lock_lock = SPIN_LOCK_UNLOCKED;
+
 static kmem_cache_t *filelock_cache;
 
+#define IS_LEASE(fl) ((fl->fl_flags & FL_LEASE) > 0)
+#define IS_POSIX(fl) ((fl->fl_flags & FL_POSIX) > 0)
+#define IS_FLOCK(fl) ((fl->fl_flags & FL_FLOCK) > 0)
+
 /* Allocate an empty lock structure. */
 static struct file_lock *locks_alloc_lock(int account)
 {
@@ -217,25 +230,44 @@
 	new->fl_u = fl->fl_u;
 }
 
+static inline int flock_translate_cmd(int cmd) {
+	if (cmd & LOCK_MAND)
+		return cmd & (LOCK_MAND | LOCK_RW);
+	switch (cmd &~ LOCK_NB) {
+	case LOCK_SH:
+		return F_RDLCK;
+	case LOCK_EX:
+		return F_WRLCK;
+	case LOCK_UN:
+		return F_UNLCK;
+	}
+	return -EINVAL;
+}
+
 /* Fill in a file_lock structure with an appropriate FLOCK lock. */
-static struct file_lock *flock_make_lock(struct file *filp, unsigned int type)
+static int flock_make_lock(struct file *filp,
+		struct file_lock **lock, unsigned int cmd)
 {
-	struct file_lock *fl = locks_alloc_lock(1);
+	struct file_lock *fl;
+	int type = flock_translate_cmd(cmd);
+	if (type < 0)
+		return type;
+	
+	fl = locks_alloc_lock(1);
 	if (fl == NULL)
-		return NULL;
+		return -ENOMEM;
 
-	fl->fl_owner = NULL;
 	fl->fl_file = filp;
 	fl->fl_pid = current->pid;
 	fl->fl_flags = FL_FLOCK;
 	fl->fl_type = type;
-	fl->fl_start = 0;
 	fl->fl_end = OFFSET_MAX;
-	fl->fl_notify = NULL;
-	fl->fl_insert = NULL;
-	fl->fl_remove = NULL;
-	
-	return fl;
+
+	if ((cmd & LOCK_NB) == 0)
+		fl->fl_type |= FL_SLEEP;
+
+	*lock = fl;
+	return 0;
 }
 
 static int assign_type(struct file_lock *fl, int type)
@@ -372,14 +404,6 @@
 	return 0;
 }
 
-/* Check if two locks overlap each other.
- */
-static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
-{
-	return ((fl1->fl_end >= fl2->fl_start) &&
-		(fl2->fl_end >= fl1->fl_start));
-}
-
 /*
  * Check whether two locks have the same owner
  * N.B. Do we need the test on PID as well as owner?
@@ -395,15 +419,20 @@
 /* Remove waiter from blocker's block list.
  * When blocker ends up pointing to itself then the list is empty.
  */
-static void locks_delete_block(struct file_lock *waiter)
+static inline void __locks_delete_block(struct file_lock *waiter)
 {
-	list_del(&waiter->fl_block);
-	INIT_LIST_HEAD(&waiter->fl_block);
-	list_del(&waiter->fl_link);
-	INIT_LIST_HEAD(&waiter->fl_link);
+	list_del_init(&waiter->fl_block);
+	list_del_init(&waiter->fl_link);
 	waiter->fl_next = NULL;
 }
 
+static void locks_delete_block(struct file_lock *waiter)
+{
+	spin_lock(&file_lock_lock);
+	__locks_delete_block(waiter);
+	spin_unlock(&file_lock_lock);
+}
+
 /* Insert waiter into blocker's block list.
  * We use a circular list so that processes can be easily woken up in
  * the order they blocked. The documentation doesn't require this but
@@ -416,44 +445,27 @@
 		printk(KERN_ERR "locks_insert_block: removing duplicated lock "
 			"(pid=%d %Ld-%Ld type=%d)\n", waiter->fl_pid,
 			waiter->fl_start, waiter->fl_end, waiter->fl_type);
-		locks_delete_block(waiter);
+		__locks_delete_block(waiter);
 	}
 	list_add_tail(&waiter->fl_block, &blocker->fl_block);
 	waiter->fl_next = blocker;
 	list_add(&waiter->fl_link, &blocked_list);
 }
 
-static inline
-void locks_notify_blocked(struct file_lock *waiter)
-{
-	if (waiter->fl_notify)
-		waiter->fl_notify(waiter);
-	else
-		wake_up(&waiter->fl_wait);
-}
-
 /* Wake up processes blocked waiting for blocker.
  * If told to wait then schedule the processes until the block list
  * is empty, otherwise empty the block list ourselves.
  */
-static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait)
+static void locks_wake_up_blocks(struct file_lock *blocker)
 {
 	while (!list_empty(&blocker->fl_block)) {
-		struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_block);
-
-		if (wait) {
-			locks_notify_blocked(waiter);
-			/* Let the blocked process remove waiter from the
-			 * block list when it gets scheduled.
-			 */
-			yield();
-		} else {
-			/* Remove waiter from the block list, because by the
-			 * time it wakes up blocker won't exist any more.
-			 */
-			locks_delete_block(waiter);
-			locks_notify_blocked(waiter);
-		}
+		struct file_lock *waiter = list_entry(blocker->fl_block.next,
+				struct file_lock, fl_block);
+		__locks_delete_block(waiter);
+		if (waiter->fl_notify)
+			waiter->fl_notify(waiter);
+		else
+			wake_up(&waiter->fl_wait);
 	}
 }
 
@@ -462,6 +474,8 @@
  */
 static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
 {
+	if (fl->fl_type == F_UNLCK)
+		return;
 	list_add(&fl->fl_link, &file_lock_list);
 
 	/* insert into file's list */
@@ -490,7 +504,7 @@
  * notify the FS that the lock has been cleared and
  * finally free the lock.
  */
-static inline void _delete_lock(struct file_lock *fl, unsigned int wait)
+static inline void _delete_lock(struct file_lock *fl)
 {
 	fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync);
 	if (fl->fl_fasync != NULL){
@@ -501,19 +515,19 @@
 	if (fl->fl_remove)
 		fl->fl_remove(fl);
 
-	locks_wake_up_blocks(fl, wait);
+	locks_wake_up_blocks(fl);
 	locks_free_lock(fl);
 }
 
 /*
  * Delete a lock and then free it.
  */
-static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait)
+static void locks_delete_lock(struct file_lock **thisfl_p)
 {
 	struct file_lock *fl = *thisfl_p;
 
 	_unhash_lock(thisfl_p);
-	_delete_lock(fl, wait);
+	_delete_lock(fl);
 }
 
 /*
@@ -532,65 +546,37 @@
 		fl->fl_type = F_UNLCK;
 		lock(fl->fl_file, F_SETLK, fl);
 	}
-	_delete_lock(fl, 0);
-}
-
-/* Determine if lock sys_fl blocks lock caller_fl. Common functionality
- * checks for shared/exclusive status of overlapping locks.
- */
-static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
-{
-	switch (caller_fl->fl_type) {
-	case F_RDLCK:
-		return (sys_fl->fl_type == F_WRLCK);
-
-	case F_WRLCK:
-		return (1);
-
-	default:
-		printk(KERN_ERR "locks_conflict(): impossible lock type - %d\n",
-		       caller_fl->fl_type);
-		break;
-	}
-	return (0);	/* This should never happen */
+	_delete_lock(fl);
 }
 
-/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific
- * checking before calling the locks_conflict().
- */
-static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
+/* Determine if lock sys_fl blocks lock caller. */
+static int posix_locks_conflict(struct file_lock *caller, struct file_lock *sys_fl)
 {
 	/* POSIX locks owned by the same process do not conflict with
 	 * each other.
 	 */
-	if (!(sys_fl->fl_flags & FL_POSIX) ||
-	    locks_same_owner(caller_fl, sys_fl))
-		return (0);
+	if (locks_same_owner(caller, sys_fl))
+		return 0;
 
 	/* Check whether they overlap */
-	if (!locks_overlap(caller_fl, sys_fl))
+	if ((caller->fl_end >= sys_fl->fl_start) && (sys_fl->fl_end >= caller->fl_start))
 		return 0;
 
-	return (locks_conflict(caller_fl, sys_fl));
+	return (caller->fl_type == F_WRLCK) || (sys_fl->fl_type == F_WRLCK);
 }
 
-/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific
- * checking before calling the locks_conflict().
- */
-static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
+/* Determine if lock sys_fl blocks lock caller. */
+static int flock_locks_conflict(struct file_lock *caller, struct file_lock *sys_fl)
 {
 	/* FLOCK locks referring to the same filp do not conflict with
 	 * each other.
 	 */
-	if (!(sys_fl->fl_flags & FL_FLOCK) ||
-	    (caller_fl->fl_file == sys_fl->fl_file))
-		return (0);
-#ifdef MSNFS
-	if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND))
+	if (caller->fl_file == sys_fl->fl_file)
+		return 0;
+	if ((caller->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND))
 		return 0;
-#endif
 
-	return (locks_conflict(caller_fl, sys_fl));
+	return (caller->fl_type == F_WRLCK) || (sys_fl->fl_type == F_WRLCK);
 }
 
 static int interruptible_sleep_on_locked(wait_queue_head_t *fl_wait, int timeout)
@@ -600,12 +586,17 @@
 
 	current->state = TASK_INTERRUPTIBLE;
 	add_wait_queue(fl_wait, &wait);
+	spin_unlock(&file_lock_lock);
+
+	/* I have no lock and I must scream */
 	if (timeout == 0)
 		schedule();
 	else
 		result = schedule_timeout(timeout);
 	if (signal_pending(current))
 		result = -ERESTARTSYS;
+
+	spin_lock(&file_lock_lock);
 	remove_wait_queue(fl_wait, &wait);
 	current->state = TASK_RUNNING;
 	return result;
@@ -616,7 +607,7 @@
 	int result;
 	locks_insert_block(blocker, waiter);
 	result = interruptible_sleep_on_locked(&waiter->fl_wait, 0);
-	locks_delete_block(waiter);
+	__locks_delete_block(waiter);
 	return result;
 }
 
@@ -625,25 +616,27 @@
 	int result;
 	locks_insert_block(blocker, waiter);
 	result = interruptible_sleep_on_locked(&waiter->fl_wait, time);
-	locks_delete_block(waiter);
+	__locks_delete_block(waiter);
 	return result;
 }
 
+/* Takes file_lock_lock */
 struct file_lock *
 posix_test_lock(struct file *filp, struct file_lock *fl)
 {
-	struct file_lock *cfl;
+	struct file_lock **lockp, *cfl = NULL;
 
-	lock_kernel();
-	for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
-		if (!(cfl->fl_flags & FL_POSIX))
+	spin_lock(&file_lock_lock);
+	for_each_lock(filp->f_dentry->d_inode, lockp) {
+		cfl = *lockp;
+		if (!IS_POSIX(cfl))
 			continue;
 		if (posix_locks_conflict(cfl, fl))
 			break;
 	}
-	unlock_kernel();
+	spin_unlock(&file_lock_lock);
 
-	return (cfl);
+	return cfl;
 }
 
 /* This function tests for deadlock condition before putting a process to
@@ -688,6 +681,26 @@
 	return 0;
 }
 
+/* This function takes no locks. */
+static int posix_find_conflict(struct inode *inode, struct file_lock *caller)
+{
+	struct file_lock **before;
+	for_each_lock(inode, before) {
+		struct file_lock *fl = *before;
+		if (!IS_POSIX(fl))
+			continue;
+		if (!posix_locks_conflict(caller, fl))
+			continue;
+		if (!(caller->fl_flags & FL_SLEEP))
+			return -EAGAIN;
+		if (posix_locks_deadlock(caller, fl))
+			return -EDEADLK;
+		locks_insert_block(fl, caller);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
 int locks_mandatory_locked(struct inode *inode)
 {
 	fl_owner_t owner = current->files;
@@ -696,14 +709,14 @@
 	/*
 	 * Search the lock list for this inode for any POSIX locks.
 	 */
-	lock_kernel();
+	spin_lock(&file_lock_lock);
 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-		if (!(fl->fl_flags & FL_POSIX))
+		if (!IS_POSIX(fl))
 			continue;
 		if (fl->fl_owner != owner)
 			break;
 	}
-	unlock_kernel();
+	spin_unlock(&file_lock_lock);
 	return fl ? -EAGAIN : 0;
 }
 
@@ -711,7 +724,6 @@
 			 struct file *filp, loff_t offset,
 			 size_t count)
 {
-	struct file_lock *fl;
 	struct file_lock *new_fl = locks_alloc_lock(0);
 	int error;
 
@@ -721,46 +733,38 @@
 	new_fl->fl_owner = current->files;
 	new_fl->fl_pid = current->pid;
 	new_fl->fl_file = filp;
-	new_fl->fl_flags = FL_POSIX | FL_ACCESS;
+	new_fl->fl_flags = FL_POSIX | FL_ACCESS | FL_SLEEP;
 	new_fl->fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
 	new_fl->fl_start = offset;
 	new_fl->fl_end = offset + count - 1;
 
 	error = 0;
-	lock_kernel();
 
-repeat:
-	/* Search the lock list for this inode for locks that conflict with
-	 * the proposed read/write.
-	 */
-	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-		if (!(fl->fl_flags & FL_POSIX))
-			continue;
-		if (fl->fl_start > new_fl->fl_end)
+	for (;;) {
+		spin_lock(&file_lock_lock);
+		error = posix_find_conflict(filp->f_dentry->d_inode, new_fl);
+		spin_unlock(&file_lock_lock);
+		if (error != -EAGAIN)
+			break;
+		error = wait_event_interruptible(new_fl->fl_wait, !new_fl->fl_next);
+
+		/* Permissions may have changed while we slept */
+		if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID) {
+			if (error) {
+				locks_delete_block(new_fl);
+			}
+			error = 0;
 			break;
-		if (posix_locks_conflict(new_fl, fl)) {
-			error = -EAGAIN;
-			if (filp && (filp->f_flags & O_NONBLOCK))
-				break;
-			error = -EDEADLK;
-			if (posix_locks_deadlock(new_fl, fl))
-				break;
-	
-			error = locks_block_on(fl, new_fl);
-			if (error != 0)
-				break;
-	
-			/*
-			 * If we've been sleeping someone might have
-			 * changed the permissions behind our back.
-			 */
-			if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID)
-				break;
-			goto repeat;
 		}
+
+		if (!error)
+			continue;
+
+		locks_delete_block(new_fl);
+		break;
 	}
+		
 	locks_free_lock(new_fl);
-	unlock_kernel();
 	return error;
 }
 
@@ -768,77 +772,125 @@
  * at the head of the list, but that's secret knowledge known only to
  * flock_lock_file and posix_lock_file.
  */
-static int flock_lock_file(struct file *filp, unsigned int lock_type,
-			   unsigned int wait)
+static int flock_lock_file(struct file *filp, struct file_lock *new_fl)
 {
-	struct file_lock *fl;
-	struct file_lock *new_fl = NULL;
 	struct file_lock **before;
 	struct inode * inode = filp->f_dentry->d_inode;
-	int error, change;
-	int unlock = (lock_type == F_UNLCK);
-
-	/*
-	 * If we need a new lock, get it in advance to avoid races.
-	 */
-	if (!unlock) {
-		error = -ENOLCK;
-		new_fl = flock_make_lock(filp, lock_type);
-		if (!new_fl)
-			return error;
-	}
+	int error = 0;
+	int found = 0;
 
-	error = 0;
-search:
-	change = 0;
-	before = &inode->i_flock;
-	while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) {
-		if (filp == fl->fl_file) {
-			if (lock_type == fl->fl_type)
-				goto out;
-			change = 1;
+	spin_lock(&file_lock_lock);
+	for_each_lock(inode, before) {
+		struct file_lock *fl = *before;
+		if (IS_POSIX(fl))
 			break;
-		}
-		before = &fl->fl_next;
-	}
-	/* change means that we are changing the type of an existing lock,
-	 * or else unlocking it.
-	 */
-	if (change) {
-		/* N.B. What if the wait argument is false? */
-		locks_delete_lock(before, !unlock);
-		/*
-		 * If we waited, another lock may have been added ...
-		 */
-		if (!unlock)
-			goto search;
+		if (IS_LEASE(fl))
+			continue;
+		if (filp != fl->fl_file)
+			continue;
+		if (new_fl->fl_type == fl->fl_type)
+			goto out;
+		found = 1;
+		locks_delete_lock(before);
+		break;
 	}
-	if (unlock)
-		goto out;
+	spin_unlock(&file_lock_lock);
+
+	if (found)
+		yield();
 
-repeat:
-	for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK);
-	     fl = fl->fl_next) {
+	if (new_fl->fl_type == F_UNLCK)
+		return 0;
+
+	spin_lock(&file_lock_lock);
+	for_each_lock(inode, before) {
+		struct file_lock *fl = *before;
+		if (IS_POSIX(fl))
+			break;
+		if (IS_LEASE(fl))
+			continue;
 		if (!flock_locks_conflict(new_fl, fl))
 			continue;
 		error = -EAGAIN;
-		if (!wait)
-			goto out;
-		error = locks_block_on(fl, new_fl);
-		if (error != 0)
-			goto out;
-		goto repeat;
+		if (new_fl->fl_flags & FL_SLEEP) {
+			locks_insert_block(fl, new_fl);
+		}
+		goto out;
 	}
-	locks_insert_lock(&inode->i_flock, new_fl);
-	new_fl = NULL;
+	locks_insert_lock(before, new_fl);
 	error = 0;
 
 out:
-	if (new_fl)
-		locks_free_lock(new_fl);
+	spin_unlock(&file_lock_lock);
 	return error;
 }
 
+/*
+ * posix_juggle_locks - Attempt to merge POSIX locks
+ * @caller: new lock
+ * @fl: existing lock
+ * @returns 0 to continue looking, 1 to stop looking and -1 to indicate
+ * that the current lock should be deleted.
+ *
+ * Attempt to merge these two locks.  Due to the heinous POSIX locking
+ * semantics, we may end up having to split an existing lock into three
+ * pieces, so we need an extra lock.
+ */
+static int posix_juggle_locks(struct file_lock *caller,
+		struct file_lock *fl, struct file_lock *extra)
+{
+	if (caller->fl_type == fl->fl_type) {
+		if (fl->fl_end < caller->fl_start - 1)
+			return 0;
+		if (fl->fl_start > caller->fl_end + 1)
+			return 1;
+
+		/* The new and old lock are of the same type and should be
+		 * merged.  Extend the new lock to be the union of the
+		 * two locks and delete the existing lock.
+		 */
+		if (fl->fl_start < caller->fl_start) {
+			caller->fl_start = fl->fl_start;
+		}
+		if (fl->fl_end > caller->fl_end) {
+			caller->fl_end = fl->fl_end;
+		}
+		return -1;
+	} else {
+		if (fl->fl_end < caller->fl_start)
+			return 0;
+		if (fl->fl_start > caller->fl_end)
+			return 1;
+
+		/* We have an overlap of some type. */
+		if (caller->fl_start <= fl->fl_start) {
+			if (fl->fl_end <= caller->fl_end) {
+				/* We completely replace this lock.  Just delete it. */
+				return -1;
+			}
+			fl->fl_start = caller->fl_end + 1;
+			/* Maybe the changed size & type allows others to progress */
+			locks_wake_up_blocks(fl);
+			return 1;
+		}
+
+		if (fl->fl_end <= caller->fl_end) {
+			fl->fl_end = caller->fl_start - 1;
+			/* Maybe the changed size & type allows others to progress */
+			locks_wake_up_blocks(fl);
+			return 1;
+		}
+		
+		/* The new lock splits the old lock.  POSIX, we hatesss it */
+		locks_copy_lock(extra, fl);
+		fl->fl_end = caller->fl_start - 1;
+		extra->fl_start = caller->fl_end + 1;
+		locks_insert_lock(&fl->fl_next, extra);
+		locks_wake_up_blocks(fl);
+		return 1;
+	}
+}
+
 /**
  *	posix_lock_file:
  *	@filp: The file to apply the lock to
@@ -848,209 +900,67 @@
  * Add a POSIX style lock to a file.
  * We merge adjacent locks whenever possible. POSIX locks are sorted by owner
  * task, then by starting address
- *
- * Kai Petzke writes:
- * To make freeing a lock much faster, we keep a pointer to the lock before the
- * actual one. But the real gain of the new coding was, that lock_it() and
- * unlock_it() became one function.
- *
- * 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 *caller,
-			   unsigned int wait)
+int posix_lock_file(struct file *filp, struct file_lock *caller)
 {
-	struct file_lock *fl;
-	struct file_lock *new_fl, *new_fl2;
-	struct file_lock *left = NULL;
-	struct file_lock *right = NULL;
-	struct file_lock **before;
-	struct inode * inode = filp->f_dentry->d_inode;
-	int error, added = 0;
+	struct file_lock *extra, **before;
+	struct inode *inode = filp->f_dentry->d_inode;
+	int error, found = 0;
 
 	/*
-	 * We may need two file_lock structures for this operation,
-	 * so we get them in advance to avoid races.
+	 * We may need an extra file_lock structure for this operation,
+	 * so get it in advance to avoid atomic allocation.
 	 */
-	new_fl = locks_alloc_lock(0);
-	new_fl2 = locks_alloc_lock(0);
 	error = -ENOLCK; /* "no luck" */
-	if (!(new_fl && new_fl2))
+	extra = locks_alloc_lock(0);
+	if (!extra)
 		goto out_nolock;
 
-	lock_kernel();
+	spin_lock(&file_lock_lock);
 	if (caller->fl_type != F_UNLCK) {
-  repeat:
-		for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-			if (!(fl->fl_flags & FL_POSIX))
-				continue;
-			if (!posix_locks_conflict(caller, fl))
-				continue;
-			error = -EAGAIN;
-			if (!wait)
-				goto out;
-			error = -EDEADLK;
-			if (posix_locks_deadlock(caller, fl))
-				goto out;
-
-			error = locks_block_on(fl, caller);
-			if (error != 0)
-				goto out;
-			goto repeat;
-  		}
+		error = posix_find_conflict(inode, caller);
+		if (error)
+			goto out;
   	}
 
-	/*
-	 * We've allocated the new locks in advance, so there are no
-	 * errors possible (and no blocking operations) from here on.
-	 * 
-	 * Find the first old lock with the same owner as the new lock.
-	 */
-	
-	before = &inode->i_flock;
-
-	/* First skip locks owned by other processes.
-	 */
-	while ((fl = *before) && (!(fl->fl_flags & FL_POSIX) ||
-				  !locks_same_owner(caller, fl))) {
-		before = &fl->fl_next;
-	}
-
-	/* Process locks with this owner.
-	 */
-	while ((fl = *before) && locks_same_owner(caller, fl)) {
-		/* Detect adjacent or overlapping regions (if same lock type)
-		 */
-		if (caller->fl_type == fl->fl_type) {
-			if (fl->fl_end < caller->fl_start - 1)
-				goto next_lock;
-			/* If the next lock in the list has entirely bigger
-			 * addresses than the new one, insert the lock here.
-			 */
-			if (fl->fl_start > caller->fl_end + 1)
+	for_each_lock(inode, before) {
+		int result;
+		struct file_lock *fl;
+ deleted:
+		fl = *before;
+		if (!IS_POSIX(fl))
+			continue;
+		if (!locks_same_owner(caller, fl)) {
+			if (found)
 				break;
-
-			/* If we come here, the new and old lock are of the
-			 * same type and adjacent or overlapping. Make one
-			 * lock yielding from the lower start address of both
-			 * locks to the higher end address.
-			 */
-			if (fl->fl_start > caller->fl_start)
-				fl->fl_start = caller->fl_start;
 			else
-				caller->fl_start = fl->fl_start;
-			if (fl->fl_end < caller->fl_end)
-				fl->fl_end = caller->fl_end;
-			else
-				caller->fl_end = fl->fl_end;
-			if (added) {
-				locks_delete_lock(before, 0);
 				continue;
-			}
-			caller = fl;
-			added = 1;
 		}
-		else {
-			/* Processing for different lock types is a bit
-			 * more complex.
-			 */
-			if (fl->fl_end < caller->fl_start)
-				goto next_lock;
-			if (fl->fl_start > caller->fl_end)
-				break;
-			if (caller->fl_type == F_UNLCK)
-				added = 1;
-			if (fl->fl_start < caller->fl_start)
-				left = fl;
-			/* If the next lock in the list has a higher end
-			 * address than the new one, insert the new one here.
-			 */
-			if (fl->fl_end > caller->fl_end) {
-				right = fl;
-				break;
-			}
-			if (fl->fl_start >= caller->fl_start) {
-				/* The new lock completely replaces an old
-				 * one (This may happen several times).
-				 */
-				if (added) {
-					locks_delete_lock(before, 0);
-					continue;
-				}
-				/* Replace the old lock with the new one.
-				 * Wake up anybody waiting for the old one,
-				 * as the change in lock type might satisfy
-				 * their needs.
-				 */
-				locks_wake_up_blocks(fl, 0);	/* This cannot schedule()! */
-				fl->fl_start = caller->fl_start;
-				fl->fl_end = caller->fl_end;
-				fl->fl_type = caller->fl_type;
-				fl->fl_u = caller->fl_u;
-				caller = fl;
-				added = 1;
-			}
+		found = 1;
+		result = posix_juggle_locks(caller, fl, extra);
+		if (result == -1) {
+			locks_delete_lock(before);
+			goto deleted;
+		} else if (result == 1) {
+			break;
 		}
-		/* Go on to next lock.
-		 */
-	next_lock:
-		before = &fl->fl_next;
 	}
 
+	locks_insert_lock(before, caller);
 	error = 0;
-	if (!added) {
-		if (caller->fl_type == F_UNLCK)
-			goto out;
-		locks_copy_lock(new_fl, caller);
-		locks_insert_lock(before, new_fl);
-		new_fl = NULL;
-	}
-	if (right) {
-		if (left == right) {
-			/* The new lock breaks the old one in two pieces,
-			 * so we have to use the second new lock.
-			 */
-			left = new_fl2;
-			new_fl2 = NULL;
-			locks_copy_lock(left, right);
-			locks_insert_lock(before, left);
-		}
-		right->fl_start = caller->fl_end + 1;
-		locks_wake_up_blocks(right, 0);
-	}
-	if (left) {
-		left->fl_end = caller->fl_start - 1;
-		locks_wake_up_blocks(left, 0);
-	}
+
 out:
-	unlock_kernel();
+	spin_unlock(&file_lock_lock);
 out_nolock:
 	/*
 	 * Free any unused locks.
 	 */
-	if (new_fl)
-		locks_free_lock(new_fl);
-	if (new_fl2)
-		locks_free_lock(new_fl2);
+	if (!extra->fl_next)
+		locks_free_lock(extra);
 	return error;
 }
 
-static inline int flock_translate_cmd(int cmd) {
-#ifdef MSNFS
-	if (cmd & LOCK_MAND)
-		return cmd & (LOCK_MAND | LOCK_RW);
-#endif
-	switch (cmd &~ LOCK_NB) {
-	case LOCK_SH:
-		return F_RDLCK;
-	case LOCK_EX:
-		return F_WRLCK;
-	case LOCK_UN:
-		return F_UNLCK;
-	}
-	return -EINVAL;
-}
-
 /**
  *	__get_lease	-	revoke all outstanding leases on file
  *	@inode: the inode of the file to return
@@ -1070,7 +980,7 @@
 
 	alloc_err = lease_alloc(NULL, 0, &new_fl);
 
-	lock_kernel();
+	spin_lock(&file_lock_lock);
 	flock = inode->i_flock;
 	if (flock->fl_type & F_INPROGRESS) {
 		if ((mode & O_NONBLOCK)
@@ -1087,7 +997,7 @@
 			if (error != 0)
 				goto out;
 			flock = inode->i_flock;
-			if (!(flock && (flock->fl_flags & FL_LEASE)))
+			if (!(flock && IS_LEASE(flock)))
 				goto out;
 		} while (flock->fl_type & F_INPROGRESS);
 	}
@@ -1112,7 +1022,7 @@
 	do {
 		fl->fl_type = future;
 		fl = fl->fl_next;
-	} while (fl != NULL && (fl->fl_flags & FL_LEASE));
+	} while (fl != NULL && IS_LEASE(fl));
 
 	kill_fasync(&flock->fl_fasync, SIGIO, POLL_MSG);
 
@@ -1129,17 +1039,17 @@
 	error = locks_block_on_timeout(flock, new_fl, error);
 	if (error == 0) {
 		/* We timed out.  Unilaterally break the lease. */
-		locks_delete_lock(&inode->i_flock, 0);
+		locks_delete_lock(&inode->i_flock);
 		printk(KERN_WARNING "lease timed out\n");
 	} else if (error > 0) {
 		flock = inode->i_flock;
-		if (flock && (flock->fl_flags & FL_LEASE))
+		if (flock && IS_LEASE(flock))
 			goto restart;
 		error = 0;
 	}
 
 out:
-	unlock_kernel();
+	spin_unlock(&file_lock_lock);
 	if (!alloc_err)
 		locks_free_lock(new_fl);
 	return error;
@@ -1156,7 +1066,7 @@
 time_t lease_get_mtime(struct inode *inode)
 {
 	struct file_lock *flock = inode->i_flock;
-	if (flock && (flock->fl_flags & FL_LEASE) && (flock->fl_type & F_WRLCK))
+	if (flock && IS_LEASE(flock) && (flock->fl_type & F_WRLCK))
 		return CURRENT_TIME;
 	return inode->i_mtime;
 }
@@ -1179,7 +1089,7 @@
 	struct file_lock *fl;
 	
 	fl = filp->f_dentry->d_inode->i_flock;
-	if ((fl == NULL) || ((fl->fl_flags & FL_LEASE) == 0))
+	if ((fl == NULL) || !IS_LEASE(fl))
 		return F_UNLCK;
 	return fl->fl_type & ~F_INPROGRESS;
 }
@@ -1192,14 +1102,14 @@
 	if (error < 0)
 		goto out;
 
-	locks_wake_up_blocks(fl, 0);
+	locks_wake_up_blocks(fl);
 
 	if (arg == F_UNLCK) {
 		filp->f_owner.pid = 0;
 		filp->f_owner.uid = 0;
 		filp->f_owner.euid = 0;
 		filp->f_owner.signum = 0;
-		locks_delete_lock(before, 0);
+		locks_delete_lock(before);
 		fasync_helper(fd, filp, 0, &fl->fl_fasync);
 	}
 
@@ -1219,7 +1129,7 @@
  */
 int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
 {
-	struct file_lock *fl, **before, **my_before = NULL;
+	struct file_lock *lock, **before, **my_before = NULL;
 	struct dentry *dentry;
 	struct inode *inode;
 	int error, rdlease_count = 0, wrlease_count = 0;
@@ -1240,12 +1150,11 @@
 		|| (atomic_read(&inode->i_count) > 1)))
 		return -EAGAIN;
 
-	before = &inode->i_flock;
-
-	lock_kernel();
+	spin_lock(&file_lock_lock);
 
-	while ((fl = *before) != NULL) {
-		if (fl->fl_flags != FL_LEASE)
+	for_each_lock(inode, before) {
+		struct file_lock *fl = *before;
+		if (!IS_LEASE(fl))
 			break;
 		if (fl->fl_file == filp)
 			my_before = before;
@@ -1253,7 +1162,6 @@
 			wrlease_count++;
 		else
 			rdlease_count++;
-		before = &fl->fl_next;
 	}
 
 	if ((arg == F_RDLCK && (wrlease_count > 0)) ||
@@ -1277,23 +1185,23 @@
 		goto out_unlock;
 	}
 
-	error = lease_alloc(filp, arg, &fl);
+	error = lease_alloc(filp, arg, &lock);
 	if (error)
 		goto out_unlock;
 
-	error = fasync_helper(fd, filp, 1, &fl->fl_fasync);
+	error = fasync_helper(fd, filp, 1, &lock->fl_fasync);
 	if (error < 0) {
-		locks_free_lock(fl);
+		locks_free_lock(lock);
 		goto out_unlock;
 	}
-	fl->fl_next = *before;
-	*before = fl;
-	list_add(&fl->fl_link, &file_lock_list);
+	lock->fl_next = *before;
+	*before = lock;
+	list_add(&lock->fl_link, &file_lock_list);
 	filp->f_owner.pid = current->pid;
 	filp->f_owner.uid = current->uid;
 	filp->f_owner.euid = current->euid;
 out_unlock:
-	unlock_kernel();
+	spin_unlock(&file_lock_lock);
 	return error;
 }
 
@@ -1319,30 +1227,36 @@
 asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
 {
 	struct file *filp;
-	int error, type;
+	struct file_lock *lock;
+	int error;
 
 	error = -EBADF;
 	filp = fget(fd);
 	if (!filp)
 		goto out;
 
-	error = flock_translate_cmd(cmd);
-	if (error < 0)
+	if ((cmd != LOCK_UN) && !(cmd & LOCK_MAND) && !(filp->f_mode & 3))
 		goto out_putf;
-	type = error;
 
-	error = -EBADF;
-	if ((type != F_UNLCK)
-#ifdef MSNFS
-		&& !(type & LOCK_MAND)
-#endif
-		&& !(filp->f_mode & 3))
+	error = flock_make_lock(filp, &lock, cmd);
+	if (error < 0)
 		goto out_putf;
 
-	lock_kernel();
-	error = flock_lock_file(filp, type,
-				(cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1);
-	unlock_kernel();
+	for (;;) {
+		error = flock_lock_file(filp, lock);
+		if ((error != -EAGAIN) || (cmd & LOCK_NB))
+			break;
+		error = wait_event_interruptible(lock->fl_wait, !lock->fl_next);
+		if (!error)
+			continue;
+
+		locks_delete_block(lock);
+		break;
+	}
+
+	if (error) {
+		locks_free_lock(lock);
+	}
 
 out_putf:
 	fput(filp);
@@ -1353,9 +1267,8 @@
 /* Report the first existing lock that would conflict with l.
  * This implements the F_GETLK command of fcntl().
  */
-int fcntl_getlk(unsigned int fd, struct flock *l)
+int fcntl_getlk(struct file *filp, struct flock *l)
 {
-	struct file *filp;
 	struct file_lock *fl, file_lock;
 	struct flock flock;
 	int error;
@@ -1367,19 +1280,14 @@
 	if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
 		goto out;
 
-	error = -EBADF;
-	filp = fget(fd);
-	if (!filp)
-		goto out;
-
 	error = flock_to_posix_lock(filp, &file_lock, &flock);
 	if (error)
-		goto out_putf;
+		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_putf;
+			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);
@@ -1399,10 +1307,10 @@
 		 */
 		error = -EOVERFLOW;
 		if (fl->fl_start > OFFT_OFFSET_MAX)
-			goto out_putf;
+			goto out;
 		if ((fl->fl_end != OFFSET_MAX)
 		    && (fl->fl_end > OFFT_OFFSET_MAX))
-			goto out_putf;
+			goto out;
 #endif
 		flock.l_start = fl->fl_start;
 		flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
@@ -1414,8 +1322,6 @@
 	if (!copy_to_user(l, &flock, sizeof(flock)))
 		error = 0;
   
-out_putf:
-	fput(filp);
 out:
 	return error;
 }
@@ -1423,9 +1329,8 @@
 /* Apply the lock described by l to an open file descriptor.
  * This implements both the F_SETLK and F_SETLKW commands of fcntl().
  */
-int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
+int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock *l)
 {
-	struct file *filp;
 	struct file_lock *file_lock = locks_alloc_lock(0);
 	struct flock flock;
 	struct inode *inode;
@@ -1441,79 +1346,65 @@
 	if (copy_from_user(&flock, l, sizeof(flock)))
 		goto out;
 
-	/* Get arguments and validate them ...
-	 */
-
-	error = -EBADF;
-	filp = fget(fd);
-	if (!filp)
-		goto out;
-
 	error = -EINVAL;
 	inode = filp->f_dentry->d_inode;
 
 	/* Don't allow mandatory locks on files that may be memory mapped
 	 * and shared.
 	 */
-	if (IS_MANDLOCK(inode) &&
-	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) {
+	if (MANDATORY_LOCK(inode)) {
 		struct address_space *mapping = inode->i_mapping;
 
 		if (!list_empty(&mapping->i_mmap_shared)) {
 			error = -EAGAIN;
-			goto out_putf;
+			goto out;
 		}
 	}
 
 	error = flock_to_posix_lock(filp, file_lock, &flock);
 	if (error)
-		goto out_putf;
+		goto out;
+	if (cmd == F_SETLKW) {
+		file_lock->fl_flags |= FL_SLEEP;
+	}
 	
 	error = -EBADF;
 	switch (flock.l_type) {
 	case F_RDLCK:
 		if (!(filp->f_mode & FMODE_READ))
-			goto out_putf;
+			goto out;
 		break;
 	case F_WRLCK:
 		if (!(filp->f_mode & FMODE_WRITE))
-			goto out_putf;
+			goto out;
 		break;
 	case F_UNLCK:
 		break;
-	case F_SHLCK:
-	case F_EXLCK:
-#ifdef __sparc__
-/* warn a bit for now, but don't overdo it */
-{
-	static int count = 0;
-	if (!count) {
-		count=1;
-		printk(KERN_WARNING
-		       "fcntl_setlk() called by process %d (%s) with broken flock() emulation\n",
-		       current->pid, current->comm);
-	}
-}
-		if (!(filp->f_mode & 3))
-			goto out_putf;
-		break;
-#endif
 	default:
 		error = -EINVAL;
-		goto out_putf;
+		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_putf;
+			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;
+
+		locks_delete_block(file_lock);
+		break;
 	}
-	error = posix_lock_file(filp, file_lock, cmd == F_SETLKW);
 
-out_putf:
-	fput(filp);
 out:
-	locks_free_lock(file_lock);
+	if (error)
+		locks_free_lock(file_lock);
 	return error;
 }
 
@@ -1521,9 +1412,8 @@
 /* Report the first existing lock that would conflict with l.
  * This implements the F_GETLK command of fcntl().
  */
-int fcntl_getlk64(unsigned int fd, struct flock64 *l)
+int fcntl_getlk64(struct file *filp, struct flock64 *l)
 {
-	struct file *filp;
 	struct file_lock *fl, file_lock;
 	struct flock64 flock;
 	int error;
@@ -1535,19 +1425,14 @@
 	if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
 		goto out;
 
-	error = -EBADF;
-	filp = fget(fd);
-	if (!filp)
-		goto out;
-
 	error = flock64_to_posix_lock(filp, &file_lock, &flock);
 	if (error)
-		goto out_putf;
+		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_putf;
+			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);
@@ -1570,8 +1455,6 @@
 	if (!copy_to_user(l, &flock, sizeof(flock)))
 		error = 0;
   
-out_putf:
-	fput(filp);
 out:
 	return error;
 }
@@ -1579,9 +1462,8 @@
 /* Apply the lock described by l to an open file descriptor.
  * This implements both the F_SETLK and F_SETLKW commands of fcntl().
  */
-int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l)
+int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 *l)
 {
-	struct file *filp;
 	struct file_lock *file_lock = locks_alloc_lock(0);
 	struct flock64 flock;
 	struct inode *inode;
@@ -1597,101 +1479,95 @@
 	if (copy_from_user(&flock, l, sizeof(flock)))
 		goto out;
 
-	/* Get arguments and validate them ...
-	 */
-
-	error = -EBADF;
-	filp = fget(fd);
-	if (!filp)
-		goto out;
-
 	error = -EINVAL;
 	inode = filp->f_dentry->d_inode;
 
 	/* Don't allow mandatory locks on files that may be memory mapped
 	 * and shared.
 	 */
-	if (IS_MANDLOCK(inode) &&
-	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) {
+	if (MANDATORY_LOCK(inode)) {
 		struct address_space *mapping = inode->i_mapping;
 
 		if (!list_empty(&mapping->i_mmap_shared)) {
 			error = -EAGAIN;
-			goto out_putf;
+			goto out;
 		}
 	}
 
 	error = flock64_to_posix_lock(filp, file_lock, &flock);
 	if (error)
-		goto out_putf;
+		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_putf;
+			goto out;
 		break;
 	case F_WRLCK:
 		if (!(filp->f_mode & FMODE_WRITE))
-			goto out_putf;
+			goto out;
 		break;
 	case F_UNLCK:
 		break;
-	case F_SHLCK:
-	case F_EXLCK:
 	default:
 		error = -EINVAL;
-		goto out_putf;
+		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_putf;
+			goto out;
+	}
+	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;
+
+		locks_delete_block(file_lock);
+		break;
 	}
-	error = posix_lock_file(filp, file_lock, cmd == F_SETLKW64);
 
-out_putf:
-	fput(filp);
 out:
-	locks_free_lock(file_lock);
+	if (error)
+		locks_free_lock(file_lock);
 	return error;
 }
 #endif /* BITS_PER_LONG == 32 */
 
 /*
  * This function is called when the file is being removed
- * from the task's fd array.
+ * from the task's fd array.  POSIX locks belonging to this task
+ * are deleted at this time.
  */
 void locks_remove_posix(struct file *filp, fl_owner_t owner)
 {
-	struct inode * inode = filp->f_dentry->d_inode;
-	struct file_lock *fl;
-	struct file_lock **before;
-
-	/*
-	 * For POSIX locks we free all locks on this file for the given task.
-	 */
-	if (!inode->i_flock) {
-		/*
-		 * Notice that something might be grabbing a lock right now.
-		 * Consider it as a race won by us - event is async, so even if
-		 * we miss the lock added we can trivially consider it as added
-		 * after we went through this call.
-		 */
-		return;
-	}
-	lock_kernel();
-	before = &inode->i_flock;
-	while ((fl = *before) != NULL) {
-		if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) {
-			locks_unlock_delete(before);
-			before = &inode->i_flock;
-			continue;
-		}
-		before = &fl->fl_next;
+	struct file_lock lock;
+	
+	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->pid;
+	lock.fl_file = filp;
+	lock.fl_notify = NULL;
+	lock.fl_insert = NULL;
+	lock.fl_remove = NULL;
+	
+	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 */
 	}
-	unlock_kernel();
+
+	posix_lock_file(filp, &lock);
 }
 
 /*
@@ -1699,25 +1575,19 @@
  */
 void locks_remove_flock(struct file *filp)
 {
-	struct inode * inode = filp->f_dentry->d_inode; 
-	struct file_lock *fl;
-	struct file_lock **before;
-
-	if (!inode->i_flock)
-		return;
-
-	lock_kernel();
-	before = &inode->i_flock;
-
-	while ((fl = *before) != NULL) {
-		if ((fl->fl_flags & (FL_FLOCK|FL_LEASE))
-		    && (fl->fl_file == filp)) {
-			locks_delete_lock(before, 0);
-			continue;
- 		}
-		before = &fl->fl_next;
-	}
-	unlock_kernel();
+	struct file_lock lock;
+	
+	lock.fl_type = F_UNLCK;
+	lock.fl_flags = FL_FLOCK;
+	lock.fl_start = 0;
+	lock.fl_end = OFFSET_MAX;
+	lock.fl_pid = current->pid;
+	lock.fl_file = filp;
+	lock.fl_notify = NULL;
+	lock.fl_insert = NULL;
+	lock.fl_remove = NULL;
+	
+	flock_lock_file(filp, &lock);
 }
 
 /**
@@ -1730,7 +1600,9 @@
 void
 posix_block_lock(struct file_lock *blocker, struct file_lock *waiter)
 {
+	spin_lock(&file_lock_lock);
 	locks_insert_block(blocker, waiter);
+	spin_unlock(&file_lock_lock);
 }
 
 /**
@@ -1742,8 +1614,10 @@
 void
 posix_unblock_lock(struct file_lock *waiter)
 {
+	spin_lock(&file_lock_lock);
 	if (!list_empty(&waiter->fl_block))
-		locks_delete_block(waiter);
+		__locks_delete_block(waiter);
+	spin_unlock(&file_lock_lock);
 }
 
 static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx)
@@ -1754,35 +1628,31 @@
 		inode = fl->fl_file->f_dentry->d_inode;
 
 	out += sprintf(out, "%d:%s ", id, pfx);
-	if (fl->fl_flags & FL_POSIX) {
+	if (IS_POSIX(fl)) {
 		out += sprintf(out, "%6s %s ",
 			     (fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
 			     (inode == NULL) ? "*NOINODE*" :
-			     (IS_MANDLOCK(inode) &&
-			      (inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ?
-			     "MANDATORY" : "ADVISORY ");
-	} else if (fl->fl_flags & FL_FLOCK) {
-#ifdef MSNFS
+			     MANDATORY_LOCK(inode) ? "MANDATORY" : "ADVISORY ");
+	} else if (IS_FLOCK(fl)) {
 		if (fl->fl_type & LOCK_MAND) {
 			out += sprintf(out, "FLOCK  MSNFS     ");
-		} else
-#endif
+		} else {
 			out += sprintf(out, "FLOCK  ADVISORY  ");
-	} else if (fl->fl_flags & FL_LEASE) {
+		}
+	} else if (IS_LEASE(fl)) {
 		out += sprintf(out, "LEASE  MANDATORY ");
 	} else {
 		out += sprintf(out, "UNKNOWN UNKNOWN  ");
 	}
-#ifdef MSNFS
 	if (fl->fl_type & LOCK_MAND) {
 		out += sprintf(out, "%s ",
 			       (fl->fl_type & LOCK_READ)
 			       ? (fl->fl_type & LOCK_WRITE) ? "RW   " : "READ "
 			       : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");
-	} else
-#endif
+	} else {
 		out += sprintf(out, "%s ",
 			       (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
+	}
 	out += sprintf(out, "%d %s:%ld ",
 		     fl->fl_pid,
 		     inode ? kdevname(inode->i_dev) : "<none>",
@@ -1834,7 +1704,7 @@
 	off_t pos = 0;
 	int i = 0;
 
-	lock_kernel();
+	spin_lock(&file_lock_lock);
 	list_for_each(tmp, &file_lock_list) {
 		struct list_head *btmp;
 		struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
@@ -1855,14 +1725,13 @@
 		}
 	}
 done:
-	unlock_kernel();
+	spin_unlock(&file_lock_lock);
 	*start = buffer;
 	if(q-buffer < length)
 		return (q-buffer);
 	return length;
 }
 
-#ifdef MSNFS
 /**
  *	lock_may_read - checks that the region is free of locks
  *	@inode: the inode that is being read
@@ -1880,14 +1749,14 @@
 {
 	struct file_lock *fl;
 	int result = 1;
-	lock_kernel();
+	spin_lock(&file_lock_lock);
 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-		if (fl->fl_flags == FL_POSIX) {
+		if (IS_POSIX(fl)) {
 			if (fl->fl_type == F_RDLCK)
 				continue;
 			if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
 				continue;
-		} else if (fl->fl_flags == FL_FLOCK) {
+		} else if (IS_FLOCK(fl)) {
 			if (!(fl->fl_type & LOCK_MAND))
 				continue;
 			if (fl->fl_type & LOCK_READ)
@@ -1897,7 +1766,7 @@
 		result = 0;
 		break;
 	}
-	unlock_kernel();
+	spin_unlock(&file_lock_lock);
 	return result;
 }
 
@@ -1918,12 +1787,12 @@
 {
 	struct file_lock *fl;
 	int result = 1;
-	lock_kernel();
+	spin_lock(&file_lock_lock);
 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-		if (fl->fl_flags == FL_POSIX) {
+		if (IS_POSIX(fl)) {
 			if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
 				continue;
-		} else if (fl->fl_flags == FL_FLOCK) {
+		} else if (IS_FLOCK(fl)) {
 			if (!(fl->fl_type & LOCK_MAND))
 				continue;
 			if (fl->fl_type & LOCK_WRITE)
@@ -1933,10 +1802,9 @@
 		result = 0;
 		break;
 	}
-	unlock_kernel();
+	spin_unlock(&file_lock_lock);
 	return result;
 }
-#endif
 
 static int __init filelock_init(void)
 {
diff -urNX dontdiff linux-2.5.15/fs/nfs/file.c linux-2.5.15-flock/fs/nfs/file.c
--- linux-2.5.15/fs/nfs/file.c	Thu May  9 16:25:16 2002
+++ linux-2.5.15-flock/fs/nfs/file.c	Tue May 14 12:28:08 2002
@@ -272,7 +272,7 @@
 	 * Not sure whether that would be unique, though, or whether
 	 * that would break in other places.
 	 */
-	if (!fl->fl_owner || (fl->fl_flags & (FL_POSIX|FL_BROKEN)) != FL_POSIX)
+	if (!fl->fl_owner || (fl->fl_flags & FL_POSIX) != FL_POSIX)
 		return -ENOLCK;
 
 	/*
diff -urNX dontdiff linux-2.5.15/include/linux/fs.h linux-2.5.15-flock/include/linux/fs.h
--- linux-2.5.15/include/linux/fs.h	Thu May  9 16:22:54 2002
+++ linux-2.5.15-flock/include/linux/fs.h	Thu May 16 14:51:02 2002
@@ -484,10 +484,10 @@
 
 #define FL_POSIX	1
 #define FL_FLOCK	2
-#define FL_BROKEN	4	/* broken flock() emulation */
-#define FL_ACCESS	8	/* for processes suspended by mandatory locking */
+#define FL_ACCESS	8	/* not trying to lock, just looking */
 #define FL_LOCKD	16	/* lock held by rpc.lockd */
 #define FL_LEASE	32	/* lease held on this file */
+#define FL_SLEEP	128	/* A blocking lock */
 
 /*
  * The POSIX file lock owner is determined by
@@ -536,11 +536,11 @@
 
 #include <linux/fcntl.h>
 
-extern int fcntl_getlk(unsigned int, struct flock *);
-extern int fcntl_setlk(unsigned int, unsigned int, struct flock *);
+extern int fcntl_getlk(struct file *, struct flock *);
+extern int fcntl_setlk(struct file *, unsigned int, struct flock *);
 
-extern int fcntl_getlk64(unsigned int, struct flock64 *);
-extern int fcntl_setlk64(unsigned int, unsigned int, struct flock64 *);
+extern int fcntl_getlk64(struct file *, struct flock64 *);
+extern int fcntl_setlk64(struct file *, unsigned int, struct flock64 *);
 
 /* fs/locks.c */
 extern void locks_init_lock(struct file_lock *);
@@ -548,7 +548,7 @@
 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 *, unsigned int);
+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_lock *);
 extern int posix_locks_deadlock(struct file_lock *, struct file_lock *);
