ipmi: cdev lock_kernel() pushdown
[safe/jmp/linux-2.6] / fs / locks.c
index faddccb..11dbf08 100644 (file)
 
 #include <linux/capability.h>
 #include <linux/file.h>
+#include <linux/fdtable.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/syscalls.h>
 #include <linux/time.h>
 #include <linux/rcupdate.h>
+#include <linux/pid_namespace.h>
 
-#include <asm/semaphore.h>
 #include <asm/uaccess.h>
 
 #define IS_POSIX(fl)   (fl->fl_flags & FL_POSIX)
@@ -185,6 +186,7 @@ void locks_init_lock(struct file_lock *fl)
        fl->fl_fasync = NULL;
        fl->fl_owner = NULL;
        fl->fl_pid = 0;
+       fl->fl_nspid = NULL;
        fl->fl_file = NULL;
        fl->fl_flags = 0;
        fl->fl_type = 0;
@@ -223,7 +225,7 @@ static void locks_copy_private(struct file_lock *new, struct file_lock *fl)
 /*
  * Initialize a new lock from an existing file_lock structure.
  */
-static void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl)
+void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl)
 {
        new->fl_owner = fl->fl_owner;
        new->fl_pid = fl->fl_pid;
@@ -235,6 +237,7 @@ static void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl)
        new->fl_ops = NULL;
        new->fl_lmops = NULL;
 }
+EXPORT_SYMBOL(__locks_copy_lock);
 
 void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
 {
@@ -553,6 +556,8 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
 {
        list_add(&fl->fl_link, &file_lock_list);
 
+       fl->fl_nspid = get_pid(task_tgid(current));
+
        /* insert into file's list */
        fl->fl_next = *pos;
        *pos = fl;
@@ -584,6 +589,11 @@ static void locks_delete_lock(struct file_lock **thisfl_p)
        if (fl->fl_ops && fl->fl_ops->fl_remove)
                fl->fl_ops->fl_remove(fl);
 
+       if (fl->fl_nspid) {
+               put_pid(fl->fl_nspid);
+               fl->fl_nspid = NULL;
+       }
+
        locks_wake_up_blocks(fl);
        locks_free_lock(fl);
 }
@@ -646,14 +656,15 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
                if (posix_locks_conflict(fl, cfl))
                        break;
        }
-       if (cfl)
+       if (cfl) {
                __locks_copy_lock(fl, cfl);
-       else
+               if (cfl->fl_nspid)
+                       fl->fl_pid = pid_vnr(cfl->fl_nspid);
+       } else
                fl->fl_type = F_UNLCK;
        unlock_kernel();
        return;
 }
-
 EXPORT_SYMBOL(posix_test_lock);
 
 /*
@@ -762,7 +773,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
         * give it the opportunity to lock the file.
         */
        if (found)
-               cond_resched();
+               cond_resched_bkl();
 
 find_conflict:
        for_each_lock(inode, before) {
@@ -824,7 +835,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
                        if (!posix_locks_conflict(request, fl))
                                continue;
                        if (conflock)
-                               locks_copy_lock(conflock, fl);
+                               __locks_copy_lock(conflock, fl);
                        error = -EAGAIN;
                        if (!(request->fl_flags & FL_SLEEP))
                                goto out;
@@ -1265,13 +1276,13 @@ out:
 EXPORT_SYMBOL(__break_lease);
 
 /**
- *     lease_get_mtime
+ *     lease_get_mtime - get the last modified time of an inode
  *     @inode: the inode
  *      @time:  pointer to a timespec which will contain the last modified time
  *
  * This is to force NFS clients to flush their caches for files with
  * exclusive leases.  The justification is that if someone has an
- * exclusive lease, then they could be modifiying it.
+ * exclusive lease, then they could be modifying it.
  */
 void lease_get_mtime(struct inode *inode, struct timespec *time)
 {
@@ -1358,18 +1369,20 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
 
        lease = *flp;
 
-       error = -EAGAIN;
-       if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
-               goto out;
-       if ((arg == F_WRLCK)
-           && ((atomic_read(&dentry->d_count) > 1)
-               || (atomic_read(&inode->i_count) > 1)))
-               goto out;
+       if (arg != F_UNLCK) {
+               error = -ENOMEM;
+               new_fl = locks_alloc_lock();
+               if (new_fl == NULL)
+                       goto out;
 
-       error = -ENOMEM;
-       new_fl = locks_alloc_lock();
-       if (new_fl == NULL)
-               goto out;
+               error = -EAGAIN;
+               if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
+                       goto out;
+               if ((arg == F_WRLCK)
+                   && ((atomic_read(&dentry->d_count) > 1)
+                       || (atomic_read(&inode->i_count) > 1)))
+                       goto out;
+       }
 
        /*
         * At this point, we know that if there is an exclusive
@@ -1395,6 +1408,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
                        rdlease_count++;
        }
 
+       error = -EAGAIN;
        if ((arg == F_RDLCK && (wrlease_count > 0)) ||
            (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0)))
                goto out;
@@ -1481,8 +1495,7 @@ EXPORT_SYMBOL_GPL(vfs_setlease);
 int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
 {
        struct file_lock fl, *flp = &fl;
-       struct dentry *dentry = filp->f_path.dentry;
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = filp->f_path.dentry->d_inode;
        int error;
 
        locks_init_lock(&fl);
@@ -1740,6 +1753,7 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
        struct file_lock *file_lock = locks_alloc_lock();
        struct flock flock;
        struct inode *inode;
+       struct file *f;
        int error;
 
        if (file_lock == NULL)
@@ -1791,24 +1805,36 @@ again:
        if (error)
                goto out;
 
-       for (;;) {
-               error = vfs_lock_file(filp, cmd, file_lock, NULL);
-               if (error != -EAGAIN || cmd == F_SETLK)
-                       break;
-               error = wait_event_interruptible(file_lock->fl_wait,
-                               !file_lock->fl_next);
-               if (!error)
-                       continue;
+       if (filp->f_op && filp->f_op->lock != NULL)
+               error = filp->f_op->lock(filp, cmd, file_lock);
+       else {
+               for (;;) {
+                       error = posix_lock_file(filp, file_lock, NULL);
+                       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;
+                       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) {
+       /*
+        * 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(&current->files->file_lock);
+       f = fcheck(fd);
+       spin_unlock(&current->files->file_lock);
+       if (!error && f != filp && flock.l_type != F_UNLCK) {
                flock.l_type = F_UNLCK;
                goto again;
        }
@@ -1864,6 +1890,7 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
        struct file_lock *file_lock = locks_alloc_lock();
        struct flock64 flock;
        struct inode *inode;
+       struct file *f;
        int error;
 
        if (file_lock == NULL)
@@ -1915,24 +1942,31 @@ again:
        if (error)
                goto out;
 
-       for (;;) {
-               error = vfs_lock_file(filp, cmd, file_lock, NULL);
-               if (error != -EAGAIN || cmd == F_SETLK64)
-                       break;
-               error = wait_event_interruptible(file_lock->fl_wait,
-                               !file_lock->fl_next);
-               if (!error)
-                       continue;
+       if (filp->f_op && filp->f_op->lock != NULL)
+               error = filp->f_op->lock(filp, cmd, file_lock);
+       else {
+               for (;;) {
+                       error = posix_lock_file(filp, file_lock, NULL);
+                       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;
+                       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) {
+       spin_lock(&current->files->file_lock);
+       f = fcheck(fd);
+       spin_unlock(&current->files->file_lock);
+       if (!error && f != filp && flock.l_type != F_UNLCK) {
                flock.l_type = F_UNLCK;
                goto again;
        }
@@ -2070,6 +2104,12 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
                                                        int id, char *pfx)
 {
        struct inode *inode = NULL;
+       unsigned int fl_pid;
+
+       if (fl->fl_nspid)
+               fl_pid = pid_vnr(fl->fl_nspid);
+       else
+               fl_pid = fl->fl_pid;
 
        if (fl->fl_file != NULL)
                inode = fl->fl_file->f_path.dentry->d_inode;
@@ -2110,16 +2150,16 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
        }
        if (inode) {
 #ifdef WE_CAN_BREAK_LSLK_NOW
-               seq_printf(f, "%d %s:%ld ", fl->fl_pid,
+               seq_printf(f, "%d %s:%ld ", fl_pid,
                                inode->i_sb->s_id, inode->i_ino);
 #else
                /* userspace relies on this representation of dev_t ;-( */
-               seq_printf(f, "%d %02x:%02x:%ld ", fl->fl_pid,
+               seq_printf(f, "%d %02x:%02x:%ld ", fl_pid,
                                MAJOR(inode->i_sb->s_dev),
                                MINOR(inode->i_sb->s_dev), inode->i_ino);
 #endif
        } else {
-               seq_printf(f, "%d <none>:0 ", fl->fl_pid);
+               seq_printf(f, "%d <none>:0 ", fl_pid);
        }
        if (IS_POSIX(fl)) {
                if (fl->fl_end == OFFSET_MAX)