[PATCH] sched: less newidle locking
[pandora-kernel.git] / fs / locks.c
index 29fa5da..c2c09b4 100644 (file)
@@ -1591,7 +1591,8 @@ out:
 /* 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(struct file *filp, unsigned int cmd, struct flock __user *l)
+int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
+               struct flock __user *l)
 {
        struct file_lock *file_lock = locks_alloc_lock();
        struct flock flock;
@@ -1620,6 +1621,7 @@ int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock __user *l)
                goto out;
        }
 
+again:
        error = flock_to_posix_lock(filp, file_lock, &flock);
        if (error)
                goto out;
@@ -1648,25 +1650,33 @@ int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock __user *l)
        if (error)
                goto out;
 
-       if (filp->f_op && filp->f_op->lock != NULL) {
+       if (filp->f_op && filp->f_op->lock != NULL)
                error = filp->f_op->lock(filp, cmd, file_lock);
-               goto out;
-       }
+       else {
+               for (;;) {
+                       error = __posix_lock_file(inode, file_lock);
+                       if ((error != -EAGAIN) || (cmd == F_SETLK))
+                               break;
+                       error = wait_event_interruptible(file_lock->fl_wait,
+                                       !file_lock->fl_next);
+                       if (!error)
+                               continue;
 
-       for (;;) {
-               error = __posix_lock_file(inode, file_lock);
-               if ((error != -EAGAIN) || (cmd == F_SETLK))
+                       locks_delete_block(file_lock);
                        break;
-               error = wait_event_interruptible(file_lock->fl_wait,
-                               !file_lock->fl_next);
-               if (!error)
-                       continue;
+               }
+       }
 
-               locks_delete_block(file_lock);
-               break;
+       /*
+        * Attempt to detect a close/fcntl race and recover by
+        * releasing the lock that was just acquired.
+        */
+       if (!error && fcheck(fd) != filp && flock.l_type != F_UNLCK) {
+               flock.l_type = F_UNLCK;
+               goto again;
        }
 
- out:
+out:
        locks_free_lock(file_lock);
        return error;
 }
@@ -1724,7 +1734,8 @@ out:
 /* 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(struct file *filp, unsigned int cmd, struct flock64 __user *l)
+int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
+               struct flock64 __user *l)
 {
        struct file_lock *file_lock = locks_alloc_lock();
        struct flock64 flock;
@@ -1753,6 +1764,7 @@ int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l)
                goto out;
        }
 
+again:
        error = flock64_to_posix_lock(filp, file_lock, &flock);
        if (error)
                goto out;
@@ -1781,22 +1793,30 @@ int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l)
        if (error)
                goto out;
 
-       if (filp->f_op && filp->f_op->lock != NULL) {
+       if (filp->f_op && filp->f_op->lock != NULL)
                error = filp->f_op->lock(filp, cmd, file_lock);
-               goto out;
-       }
+       else {
+               for (;;) {
+                       error = __posix_lock_file(inode, file_lock);
+                       if ((error != -EAGAIN) || (cmd == F_SETLK64))
+                               break;
+                       error = wait_event_interruptible(file_lock->fl_wait,
+                                       !file_lock->fl_next);
+                       if (!error)
+                               continue;
 
-       for (;;) {
-               error = __posix_lock_file(inode, file_lock);
-               if ((error != -EAGAIN) || (cmd == F_SETLK64))
+                       locks_delete_block(file_lock);
                        break;
-               error = wait_event_interruptible(file_lock->fl_wait,
-                               !file_lock->fl_next);
-               if (!error)
-                       continue;
+               }
+       }
 
-               locks_delete_block(file_lock);
-               break;
+       /*
+        * Attempt to detect a close/fcntl race and recover by
+        * releasing the lock that was just acquired.
+        */
+       if (!error && fcheck(fd) != filp && flock.l_type != F_UNLCK) {
+               flock.l_type = F_UNLCK;
+               goto again;
        }
 
 out:
@@ -1888,12 +1908,7 @@ void locks_remove_flock(struct file *filp)
 
        while ((fl = *before) != NULL) {
                if (fl->fl_file == filp) {
-                       /*
-                        * We might have a POSIX lock that was created at the same time
-                        * the filp was closed for the last time. Just remove that too,
-                        * regardless of ownership, since nobody can own it.
-                        */
-                       if (IS_FLOCK(fl) || IS_POSIX(fl)) {
+                       if (IS_FLOCK(fl)) {
                                locks_delete_lock(before);
                                continue;
                        }
@@ -2183,21 +2198,23 @@ void steal_locks(fl_owner_t from)
 {
        struct files_struct *files = current->files;
        int i, j;
+       struct fdtable *fdt;
 
        if (from == files)
                return;
 
        lock_kernel();
        j = 0;
+       fdt = files_fdtable(files);
        for (;;) {
                unsigned long set;
                i = j * __NFDBITS;
-               if (i >= files->max_fdset || i >= files->max_fds)
+               if (i >= fdt->max_fdset || i >= fdt->max_fds)
                        break;
-               set = files->open_fds->fds_bits[j++];
+               set = fdt->open_fds->fds_bits[j++];
                while (set) {
                        if (set & 1) {
-                               struct file *file = files->fd[i];
+                               struct file *file = fdt->fd[i];
                                if (file)
                                        __steal_locks(file, from);
                        }