--- /dev/null
+From 7f3697e24dc3820b10f445a4a7d914fc356012d1 Mon Sep 17 00:00:00 2001
+From: Jeff Layton <jeff.layton@primarydata.com>
+Date: Thu, 7 Jan 2016 16:38:10 -0500
+Subject: locks: fix unlock when fcntl_setlk races with a close
+
+From: Jeff Layton <jeff.layton@primarydata.com>
+
+commit 7f3697e24dc3820b10f445a4a7d914fc356012d1 upstream.
+
+Dmitry reported that he was able to reproduce the WARN_ON_ONCE that
+fires in locks_free_lock_context when the flc_posix list isn't empty.
+
+The problem turns out to be that we're basically rebuilding the
+file_lock from scratch in fcntl_setlk when we discover that the setlk
+has raced with a close. If the l_whence field is SEEK_CUR or SEEK_END,
+then we may end up with fl_start and fl_end values that differ from
+when the lock was initially set, if the file position or length of the
+file has changed in the interim.
+
+Fix this by just reusing the same lock request structure, and simply
+override fl_type value with F_UNLCK as appropriate. That ensures that
+we really are unlocking the lock that was initially set.
+
+While we're there, make sure that we do pop a WARN_ON_ONCE if the
+removal ever fails. Also return -EBADF in this event, since that's
+what we would have returned if the close had happened earlier.
+
+Cc: Alexander Viro <viro@zeniv.linux.org.uk>
+Fixes: c293621bbf67 (stale POSIX lock handling)
+Reported-by: Dmitry Vyukov <dvyukov@google.com>
+Signed-off-by: Jeff Layton <jeff.layton@primarydata.com>
+Acked-by: "J. Bruce Fields" <bfields@fieldses.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/locks.c | 51 ++++++++++++++++++++++++++++++---------------------
+ 1 file changed, 30 insertions(+), 21 deletions(-)
+
+--- a/fs/locks.c
++++ b/fs/locks.c
+@@ -1852,7 +1852,6 @@ int fcntl_setlk(unsigned int fd, struct
+ goto out;
+ }
+
+-again:
+ error = flock_to_posix_lock(filp, file_lock, &flock);
+ if (error)
+ goto out;
+@@ -1883,19 +1882,22 @@ again:
+ * Attempt to detect a close/fcntl race and recover by
+ * releasing the lock that was just acquired.
+ */
+- /*
+- * we need that spin_lock here - it prevents reordering between
+- * update of inode->i_flock and check for it done in close().
+- * rcu_read_lock() wouldn't do.
+- */
+- spin_lock(¤t->files->file_lock);
+- f = fcheck(fd);
+- spin_unlock(¤t->files->file_lock);
+- if (!error && f != filp && flock.l_type != F_UNLCK) {
+- flock.l_type = F_UNLCK;
+- goto again;
++ if (!error && file_lock->fl_type != F_UNLCK) {
++ /*
++ * We need that spin_lock here - it prevents reordering between
++ * update of inode->i_flock and check for it done in
++ * close(). rcu_read_lock() wouldn't do.
++ */
++ spin_lock(¤t->files->file_lock);
++ f = fcheck(fd);
++ spin_unlock(¤t->files->file_lock);
++ if (f != filp) {
++ file_lock->fl_type = F_UNLCK;
++ error = do_lock_file_wait(filp, cmd, file_lock);
++ WARN_ON_ONCE(error);
++ error = -EBADF;
++ }
+ }
+-
+ out:
+ locks_free_lock(file_lock);
+ return error;
+@@ -1970,7 +1972,6 @@ int fcntl_setlk64(unsigned int fd, struc
+ goto out;
+ }
+
+-again:
+ error = flock64_to_posix_lock(filp, file_lock, &flock);
+ if (error)
+ goto out;
+@@ -2001,14 +2002,22 @@ again:
+ * Attempt to detect a close/fcntl race and recover by
+ * releasing the lock that was just acquired.
+ */
+- spin_lock(¤t->files->file_lock);
+- f = fcheck(fd);
+- spin_unlock(¤t->files->file_lock);
+- if (!error && f != filp && flock.l_type != F_UNLCK) {
+- flock.l_type = F_UNLCK;
+- goto again;
++ if (!error && file_lock->fl_type != F_UNLCK) {
++ /*
++ * We need that spin_lock here - it prevents reordering between
++ * update of inode->i_flock and check for it done in
++ * close(). rcu_read_lock() wouldn't do.
++ */
++ spin_lock(¤t->files->file_lock);
++ f = fcheck(fd);
++ spin_unlock(¤t->files->file_lock);
++ if (f != filp) {
++ file_lock->fl_type = F_UNLCK;
++ error = do_lock_file_wait(filp, cmd, file_lock);
++ WARN_ON_ONCE(error);
++ error = -EBADF;
++ }
+ }
+-
+ out:
+ locks_free_lock(file_lock);
+ return error;