futex: Add sys_futex_wake()
[linux-2.6-block.git] / kernel / futex / syscalls.c
index a8074079b09e87ca867c2cfc84be021076198a65..7049a52ef68e0ff9d34ca4738a76200efbedb7ff 100644 (file)
@@ -1,6 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
-#include <linux/compat.h>
 #include <linux/syscalls.h>
 #include <linux/time_namespace.h>
 
@@ -85,15 +84,12 @@ err_unlock:
 long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
                u32 __user *uaddr2, u32 val2, u32 val3)
 {
+       unsigned int flags = futex_to_flags(op);
        int cmd = op & FUTEX_CMD_MASK;
-       unsigned int flags = 0;
 
-       if (!(op & FUTEX_PRIVATE_FLAG))
-               flags |= FLAGS_SHARED;
-
-       if (op & FUTEX_CLOCK_REALTIME) {
-               flags |= FLAGS_CLOCKRT;
-               if (cmd != FUTEX_WAIT_BITSET && cmd != FUTEX_WAIT_REQUEUE_PI &&
+       if (flags & FLAGS_CLOCKRT) {
+               if (cmd != FUTEX_WAIT_BITSET &&
+                   cmd != FUTEX_WAIT_REQUEUE_PI &&
                    cmd != FUTEX_LOCK_PI2)
                        return -ENOSYS;
        }
@@ -183,8 +179,7 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
        return do_futex(uaddr, op, val, tp, uaddr2, (unsigned long)utime, val3);
 }
 
-/* Mask of available flags for each futex in futex_waitv list */
-#define FUTEXV_WAITER_MASK (FUTEX_32 | FUTEX_PRIVATE_FLAG)
+#define FUTEX2_VALID_MASK (FUTEX2_SIZE_MASK | FUTEX2_PRIVATE)
 
 /**
  * futex_parse_waitv - Parse a waitv array from userspace
@@ -202,16 +197,22 @@ static int futex_parse_waitv(struct futex_vector *futexv,
        unsigned int i;
 
        for (i = 0; i < nr_futexes; i++) {
+               unsigned int flags;
+
                if (copy_from_user(&aux, &uwaitv[i], sizeof(aux)))
                        return -EFAULT;
 
-               if ((aux.flags & ~FUTEXV_WAITER_MASK) || aux.__reserved)
+               if ((aux.flags & ~FUTEX2_VALID_MASK) || aux.__reserved)
+                       return -EINVAL;
+
+               flags = futex2_to_flags(aux.flags);
+               if (!futex_flags_valid(flags))
                        return -EINVAL;
 
-               if (!(aux.flags & FUTEX_32))
+               if (!futex_validate_input(flags, aux.val))
                        return -EINVAL;
 
-               futexv[i].w.flags = aux.flags;
+               futexv[i].w.flags = flags;
                futexv[i].w.val = aux.val;
                futexv[i].w.uaddr = aux.uaddr;
                futexv[i].q = futex_q_init;
@@ -305,6 +306,36 @@ destroy_timer:
        return ret;
 }
 
+/*
+ * sys_futex_wake - Wake a number of futexes
+ * @uaddr:     Address of the futex(es) to wake
+ * @mask:      bitmask
+ * @nr:                Number of the futexes to wake
+ * @flags:     FUTEX2 flags
+ *
+ * Identical to the traditional FUTEX_WAKE_BITSET op, except it is part of the
+ * futex2 family of calls.
+ */
+
+SYSCALL_DEFINE4(futex_wake,
+               void __user *, uaddr,
+               unsigned long, mask,
+               int, nr,
+               unsigned int, flags)
+{
+       if (flags & ~FUTEX2_VALID_MASK)
+               return -EINVAL;
+
+       flags = futex2_to_flags(flags);
+       if (!futex_flags_valid(flags))
+               return -EINVAL;
+
+       if (!futex_validate_input(flags, mask))
+               return -EINVAL;
+
+       return futex_wake(uaddr, flags, nr, mask);
+}
+
 #ifdef CONFIG_COMPAT
 COMPAT_SYSCALL_DEFINE2(set_robust_list,
                struct compat_robust_list_head __user *, head,