Merge branch 'for-2.6.30' of git://linux-nfs.org/~bfields/linux
[pandora-kernel.git] / fs / fcntl.c
index bd215cc..cc8e4de 100644 (file)
@@ -141,7 +141,7 @@ SYSCALL_DEFINE1(dup, unsigned int, fildes)
        return ret;
 }
 
-#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT | O_NOATIME)
+#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)
 
 static int setfl(int fd, struct file * filp, unsigned long arg)
 {
@@ -177,21 +177,21 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
                return error;
 
        /*
-        * We still need a lock here for now to keep multiple FASYNC calls
-        * from racing with each other.
+        * ->fasync() is responsible for setting the FASYNC bit.
         */
-       lock_kernel();
-       if ((arg ^ filp->f_flags) & FASYNC) {
-               if (filp->f_op && filp->f_op->fasync) {
-                       error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
-                       if (error < 0)
-                               goto out;
-               }
+       if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op &&
+                       filp->f_op->fasync) {
+               error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
+               if (error < 0)
+                       goto out;
+               if (error > 0)
+                       error = 0;
        }
-
+       spin_lock(&filp->f_lock);
        filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK);
+       spin_unlock(&filp->f_lock);
+
  out:
-       unlock_kernel();
        return error;
 }
 
@@ -516,7 +516,7 @@ static DEFINE_RWLOCK(fasync_lock);
 static struct kmem_cache *fasync_cache __read_mostly;
 
 /*
- * fasync_helper() is used by some character device drivers (mainly mice)
+ * fasync_helper() is used by almost all character device drivers
  * to set up the fasync queue. It returns negative on error, 0 if it did
  * no changes and positive if it added/deleted the entry.
  */
@@ -531,6 +531,12 @@ int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fap
                if (!new)
                        return -ENOMEM;
        }
+
+       /*
+        * We need to take f_lock first since it's not an IRQ-safe
+        * lock.
+        */
+       spin_lock(&filp->f_lock);
        write_lock_irq(&fasync_lock);
        for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
                if (fa->fa_file == filp) {
@@ -555,7 +561,12 @@ int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fap
                result = 1;
        }
 out:
+       if (on)
+               filp->f_flags |= FASYNC;
+       else
+               filp->f_flags &= ~FASYNC;
        write_unlock_irq(&fasync_lock);
+       spin_unlock(&filp->f_lock);
        return result;
 }