Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / fs / locks.c
index c525aa4..3b0d05d 100644 (file)
@@ -60,7 +60,7 @@
  *
  *  Initial implementation of mandatory locks. SunOS turned out to be
  *  a rotten model, so I implemented the "obvious" semantics.
- *  See 'Documentation/mandatory.txt' for details.
+ *  See 'Documentation/filesystems/mandatory-locking.txt' for details.
  *  Andy Walker (andy@lysaker.kvaerner.no), April 06, 1996.
  *
  *  Don't allow mandatory locks on mmap()'ed files. Added simple functions to
@@ -1352,50 +1352,23 @@ int fcntl_getlease(struct file *filp)
        return type;
 }
 
-/**
- *     generic_setlease        -       sets a lease on an open file
- *     @filp: file pointer
- *     @arg: type of lease to obtain
- *     @flp: input - file_lock to use, output - file_lock inserted
- *
- *     The (input) flp->fl_lmops->lm_break function is required
- *     by break_lease().
- *
- *     Called with file_lock_lock held.
- */
-int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
+int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
 {
        struct file_lock *fl, **before, **my_before = NULL, *lease;
        struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
-       int error, rdlease_count = 0, wrlease_count = 0;
+       int error;
 
        lease = *flp;
 
-       error = -EACCES;
-       if ((current_fsuid() != inode->i_uid) && !capable(CAP_LEASE))
-               goto out;
-       error = -EINVAL;
-       if (!S_ISREG(inode->i_mode))
+       error = -EAGAIN;
+       if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
                goto out;
-       error = security_file_lock(filp, arg);
-       if (error)
+       if ((arg == F_WRLCK)
+           && ((dentry->d_count > 1)
+               || (atomic_read(&inode->i_count) > 1)))
                goto out;
 
-       time_out_leases(inode);
-
-       BUG_ON(!(*flp)->fl_lmops->lm_break);
-
-       if (arg != F_UNLCK) {
-               error = -EAGAIN;
-               if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
-                       goto out;
-               if ((arg == F_WRLCK)
-                   && ((dentry->d_count > 1)
-                       || (atomic_read(&inode->i_count) > 1)))
-                       goto out;
-       }
-
        /*
         * At this point, we know that if there is an exclusive
         * lease on this file, then we hold it on this filp
@@ -1404,27 +1377,28 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
         * then the file is not open by anyone (including us)
         * except for this filp.
         */
+       error = -EAGAIN;
        for (before = &inode->i_flock;
                        ((fl = *before) != NULL) && IS_LEASE(fl);
                        before = &fl->fl_next) {
-               if (fl->fl_file == filp)
+               if (fl->fl_file == filp) {
                        my_before = before;
-               else if (fl->fl_flags & FL_UNLOCK_PENDING)
-                       /*
-                        * Someone is in the process of opening this
-                        * file for writing so we may not take an
-                        * exclusive lease on it.
-                        */
-                       wrlease_count++;
-               else
-                       rdlease_count++;
+                       continue;
+               }
+               /*
+                * No exclusive leases if someone else has a lease on
+                * this file:
+                */
+               if (arg == F_WRLCK)
+                       goto out;
+               /*
+                * Modifying our existing lease is OK, but no getting a
+                * new lease if someone else is opening for write:
+                */
+               if (fl->fl_flags & FL_UNLOCK_PENDING)
+                       goto out;
        }
 
-       error = -EAGAIN;
-       if ((arg == F_RDLCK && (wrlease_count > 0)) ||
-           (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0)))
-               goto out;
-
        if (my_before != NULL) {
                error = lease->fl_lmops->lm_change(my_before, arg);
                if (!error)
@@ -1432,9 +1406,6 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
                goto out;
        }
 
-       if (arg == F_UNLCK)
-               goto out;
-
        error = -EINVAL;
        if (!leases_enable)
                goto out;
@@ -1445,6 +1416,62 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
 out:
        return error;
 }
+
+int generic_delete_lease(struct file *filp, struct file_lock **flp)
+{
+       struct file_lock *fl, **before;
+       struct dentry *dentry = filp->f_path.dentry;
+       struct inode *inode = dentry->d_inode;
+
+       for (before = &inode->i_flock;
+                       ((fl = *before) != NULL) && IS_LEASE(fl);
+                       before = &fl->fl_next) {
+               if (fl->fl_file != filp)
+                       continue;
+               return (*flp)->fl_lmops->lm_change(before, F_UNLCK);
+       }
+       return -EAGAIN;
+}
+
+/**
+ *     generic_setlease        -       sets a lease on an open file
+ *     @filp: file pointer
+ *     @arg: type of lease to obtain
+ *     @flp: input - file_lock to use, output - file_lock inserted
+ *
+ *     The (input) flp->fl_lmops->lm_break function is required
+ *     by break_lease().
+ *
+ *     Called with file_lock_lock held.
+ */
+int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
+{
+       struct dentry *dentry = filp->f_path.dentry;
+       struct inode *inode = dentry->d_inode;
+       int error;
+
+       if ((current_fsuid() != inode->i_uid) && !capable(CAP_LEASE))
+               return -EACCES;
+       if (!S_ISREG(inode->i_mode))
+               return -EINVAL;
+       error = security_file_lock(filp, arg);
+       if (error)
+               return error;
+
+       time_out_leases(inode);
+
+       BUG_ON(!(*flp)->fl_lmops->lm_break);
+
+       switch (arg) {
+       case F_UNLCK:
+               return generic_delete_lease(filp, flp);
+       case F_RDLCK:
+       case F_WRLCK:
+               return generic_add_lease(filp, arg, flp);
+       default:
+               BUG();
+       }
+}
 EXPORT_SYMBOL(generic_setlease);
 
 static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease)