fedora core 2.6.10-1.12-FC2
[linux-2.6.git] / kernel / futex.c
index f7afaf5..645a430 100644 (file)
@@ -6,7 +6,7 @@
  *  (C) Copyright 2003 Red Hat Inc, All Rights Reserved
  *
  *  Removed page pinning, fix privately mapped COW pages and other cleanups
- *  (C) Copyright 2003 Jamie Lokier
+ *  (C) Copyright 2003, 2004 Jamie Lokier
  *
  *  Thanks to Ben LaHaise for yelling "hashed waitqueues" loudly
  *  enough at me, Linus for the original (flawed) idea, Matthew
@@ -38,6 +38,7 @@
 #include <linux/futex.h>
 #include <linux/mount.h>
 #include <linux/pagemap.h>
+#include <linux/syscalls.h>
 
 #define FUTEX_HASHBITS 8
 
@@ -275,7 +276,7 @@ static void wake_futex(struct futex_q *q)
         * The waiting task can free the futex_q as soon as this is written,
         * without taking any locks.  This must come last.
         */
-       q->lock_ptr = 0;
+       q->lock_ptr = NULL;
 }
 
 /*
@@ -354,7 +355,7 @@ static int futex_requeue(unsigned long uaddr1, unsigned long uaddr2,
                   before *uaddr1.  */
                smp_mb();
 
-               if (get_user(curval, (int *)uaddr1) != 0) {
+               if (get_user(curval, (int __user *)uaddr1) != 0) {
                        ret = -EFAULT;
                        goto out;
                }
@@ -488,9 +489,24 @@ static int futex_wait(unsigned long uaddr, int val, unsigned long time)
        queue_me(&q, -1, NULL);
 
        /*
-        * Access the page after the futex is queued.
+        * Access the page AFTER the futex is queued.
+        * Order is important:
+        *
+        *   Userspace waiter: val = var; if (cond(val)) futex_wait(&var, val);
+        *   Userspace waker:  if (cond(var)) { var = new; futex_wake(&var); }
+        *
+        * The basic logical guarantee of a futex is that it blocks ONLY
+        * if cond(var) is known to be true at the time of blocking, for
+        * any cond.  If we queued after testing *uaddr, that would open
+        * a race condition where we could block indefinitely with
+        * cond(var) false, which would violate the guarantee.
+        *
+        * A consequence is that futex_wait() can return zero and absorb
+        * a wakeup when *uaddr != val on entry to the syscall.  This is
+        * rare, but normal.
+        *
         * We hold the mmap semaphore, so the mapping cannot have changed
-        * since we looked it up.
+        * since we looked it up in get_futex_key.
         */
        if (get_user(curval, (int __user *)uaddr) != 0) {
                ret = -EFAULT;
@@ -537,8 +553,8 @@ static int futex_wait(unsigned long uaddr, int val, unsigned long time)
                return 0;
        if (time == 0)
                return -ETIMEDOUT;
-       /* A spurious wakeup should never happen. */
-       WARN_ON(!signal_pending(current));
+       /* We expect signal_pending(current), but another thread may
+        * have handled it for us already. */
        return -EINTR;
 
  out_unqueue:
@@ -703,7 +719,7 @@ asmlinkage long sys_futex(u32 __user *uaddr, int op, int val,
         * requeue parameter in 'utime' if op == FUTEX_REQUEUE.
         */
        if (op >= FUTEX_REQUEUE)
-               val2 = (int) (long) utime;
+               val2 = (int) (unsigned long) utime;
 
        return do_futex((unsigned long)uaddr, op, val, timeout,
                        (unsigned long)uaddr2, val2, val3);
@@ -731,7 +747,7 @@ static int __init init(void)
 
        for (i = 0; i < ARRAY_SIZE(futex_queues); i++) {
                INIT_LIST_HEAD(&futex_queues[i].chain);
-               futex_queues[i].lock = SPIN_LOCK_UNLOCKED;
+               spin_lock_init(&futex_queues[i].lock);
        }
        return 0;
 }