diff -urNX dontdiff linux-2.5.22/fs/lockd/svclock.c linux-2.5.22-flock/fs/lockd/svclock.c
--- linux-2.5.22/fs/lockd/svclock.c     Sun Jun  2 18:44:45 2002
+++ linux-2.5.22-flock/fs/lockd/svclock.c       Fri Jun 14 06:19:17 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.22/fs/lockd/svcsubs.c linux-2.5.22-flock/fs/lockd/svcsubs.c
--- linux-2.5.22/fs/lockd/svcsubs.c     Sun Jun  2 18:44:41 2002
+++ linux-2.5.22-flock/fs/lockd/svcsubs.c       Fri Jun 14 06:19:17 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.22/fs/locks.c linux-2.5.22-flock/fs/locks.c
--- linux-2.5.22/fs/locks.c     Mon Jun 17 04:49:08 2002
+++ linux-2.5.22-flock/fs/locks.c       Tue Jun 18 12:55:44 2002
@@ -184,7 +184,6 @@
       fl->fl_file = NULL;
       fl->fl_flags = 0;
       fl->fl_type = 0;
-       fl->fl_start = fl->fl_end = 0;
       fl->fl_notify = NULL;
       fl->fl_insert = NULL;
       fl->fl_remove = NULL;
@@ -254,8 +253,12 @@
       fl->fl_pid = current->pid;
       fl->fl_flags = FL_FLOCK;
       fl->fl_type = type;
+       fl->fl_start = 0;
       fl->fl_end = OFFSET_MAX;
-
+
+       if ((cmd & LOCK_NB) == 0)
+               fl->fl_flags |= FL_SLEEP;
+
       *lock = fl;
       return 0;
}
@@ -465,6 +468,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 */
@@ -767,8 +772,7 @@
 * 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, struct file_lock *new_fl,
-                          unsigned int wait)
+static int flock_lock_file(struct file *filp, struct file_lock *new_fl)
{
       struct file_lock **before;
       struct inode * inode = filp->f_dentry->d_inode;
@@ -799,7 +803,6 @@
               return 0;

       lock_kernel();
-repeat:
       for_each_lock(inode, before) {
               struct file_lock *fl = *before;
               if (IS_POSIX(fl))
@@ -809,12 +812,10 @@
               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);
       error = 0;
@@ -824,199 +825,174 @@
       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;
+
+               printk(KERN_DEBUG "locks: caller %d %Lx-%Lx, existing %d %Lx-%Lx\n",
+                               caller->fl_type, caller->fl_start, caller->fl_end,
+                               fl->fl_type, fl->fl_start, fl->fl_end);
+               /* 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
- *     @caller: The lock to be applied
- *     @wait: 1 to retry automatically, 0 to return -EAGAIN
+ * __posix_lock_file:
+ * @filp: The file to apply the lock to
+ * @caller: The lock to be applied
 *
 * 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.
+ * Locks are merged and split as appropriate.
+ * POSIX locks are sorted by owner task, then by starting address
 */

-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();
       if (caller->fl_type != F_UNLCK) {
-  repeat:
-               for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+               for_each_lock(inode, before) {
+                       struct file_lock *fl = *before;
                       if (!IS_POSIX(fl))
                               continue;
                       if (!posix_locks_conflict(caller, fl))
                               continue;
                       error = -EAGAIN;
-                       if (!wait)
+                       if (!(caller->fl_flags & FL_SLEEP))
                               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 = -EAGAIN;
+                       locks_insert_block(fl, caller);
+                       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) && (!IS_POSIX(fl) ||
-                                 !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);
                               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);
-                                       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);
-                               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 == 0)
+                       continue;
+               if (result == -1) {
+                       locks_delete_lock(before);
+                       if (*before)
+                               goto deleted;
               }
-               /* Go on to next lock.
-                */
-       next_lock:
-               before = &fl->fl_next;
+               break;
       }

+       /* If we had to use the extra lock, we shouldn't free it */
+       if (!list_empty(&extra->fl_link))
+               extra = NULL;
+       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);
-       }
-       if (left) {
-               left->fl_end = caller->fl_start - 1;
-               locks_wake_up_blocks(left);
-       }
+
out:
       unlock_kernel();
out_nolock:
       /*
        * Free any unused locks.
        */
-       if (new_fl)
-               locks_free_lock(new_fl);
-       if (new_fl2)
-               locks_free_lock(new_fl2);
+       if (extra)
+               locks_free_lock(extra);
+       return error;
+}
+
+/* Called from nfsd/lockd.  They embed the struct file_lock in their
+ * own structs, so if we use it directly we'll try and free it incorrectly
+ */
+int posix_lock_file(struct file *filp, struct file_lock *caller)
+{
+       int error;
+       struct file_lock *copy = locks_alloc_lock(0);
+       locks_copy_lock(copy, caller);
+       error = __posix_lock_file(filp, copy);
+       if (error) {
+               locks_free_lock(copy);
+       }
       return error;
}

@@ -1303,11 +1279,23 @@
       if (error < 0)
               goto out_putf;

-       error = flock_lock_file(filp, lock,
-                               (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1);
+       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;

-       if (error)
+               lock_kernel();
+               locks_delete_block(lock);
+               unlock_kernel();
+               break;
+       }
+
+       if (error) {
               locks_free_lock(lock);
+       }

out_putf:
       fput(filp);
@@ -1415,6 +1403,9 @@
       error = flock_to_posix_lock(filp, file_lock, &flock);
       if (error)
               goto out;
+       if (cmd == F_SETLKW) {
+               file_lock->fl_flags |= FL_SLEEP;
+       }

       error = -EBADF;
       switch (flock.l_type) {
@@ -1438,10 +1429,26 @@
               if (error < 0)
                       goto out;
       }
-       error = posix_lock_file(filp, file_lock, cmd == F_SETLKW);
+
+       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;
+       }

out:
-       locks_free_lock(file_lock);
+       if (error) {
+               locks_free_lock(file_lock);
+       }
       return error;
}

@@ -1534,6 +1541,9 @@
       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) {
@@ -1557,10 +1567,27 @@
               if (error < 0)
                       goto out;
       }
-       error = posix_lock_file(filp, file_lock, cmd == F_SETLKW64);
+
+       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;
+       }
+

out:
-       locks_free_lock(file_lock);
+       if (error) {
+               locks_free_lock(file_lock);
+       }
       return error;
}
#endif /* BITS_PER_LONG == 32 */
diff -urNX dontdiff linux-2.5.22/include/linux/fs.h linux-2.5.22-flock/include/linux/fs.h
--- linux-2.5.22/include/linux/fs.h     Mon Jun 17 04:49:09 2002
+++ linux-2.5.22-flock/include/linux/fs.h       Mon Jun 17 04:55:10 2002
@@ -519,10 +519,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
@@ -583,7 +583,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 *);