@@ -458,3 +458,4 @@
442 common mount_setattr sys_mount_setattr
443 common futex_wait sys_futex_wait
444 common futex_wake sys_futex_wake
+445 common futex_waitv sys_futex_waitv
@@ -38,7 +38,7 @@
#define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5)
#define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800)
-#define __NR_compat_syscalls 445
+#define __NR_compat_syscalls 446
#endif
#define __ARCH_WANT_SYS_CLONE
@@ -897,6 +897,8 @@ __SYSCALL(__NR_mount_setattr, sys_mount_setattr)
__SYSCALL(__NR_futex_wait, sys_futex_wait)
#define __NR_futex_wake 444
__SYSCALL(__NR_futex_wake, sys_futex_wake)
+#define __NR_futex_waitv 445
+__SYSCALL(__NR_futex_waitv, compat_sys_futex_waitv)
/*
* Please add new compat syscalls above this comment and update
@@ -449,3 +449,4 @@
442 i386 mount_setattr sys_mount_setattr
443 i386 futex_wait sys_futex_wait
444 i386 futex_wake sys_futex_wake
+445 i386 futex_waitv sys_futex_waitv compat_sys_futex_waitv
@@ -366,6 +366,7 @@
442 common mount_setattr sys_mount_setattr
443 common futex_wait sys_futex_wait
444 common futex_wake sys_futex_wake
+445 common futex_waitv sys_futex_waitv
#
# Due to a historical design error, certain syscalls are numbered differently
@@ -365,6 +365,12 @@ struct compat_robust_list_head {
compat_uptr_t list_op_pending;
};
+struct compat_futex_waitv {
+ compat_uptr_t uaddr;
+ compat_uint_t val;
+ compat_uint_t flags;
+};
+
#ifdef CONFIG_COMPAT_OLD_SIGACTION
struct compat_old_sigaction {
compat_uptr_t sa_handler;
@@ -654,6 +660,11 @@ asmlinkage long
compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
compat_size_t __user *len_ptr);
+/* kernel/futex2.c */
+asmlinkage long compat_sys_futex_waitv(struct compat_futex_waitv *waiters,
+ compat_uint_t nr_futexes, compat_uint_t flags,
+ struct __kernel_timespec __user *timo);
+
/* kernel/itimer.c */
asmlinkage long compat_sys_getitimer(int which,
struct old_itimerval32 __user *it);
@@ -69,6 +69,7 @@ struct io_uring_params;
struct clone_args;
struct open_how;
struct mount_attr;
+struct futex_waitv;
#include <linux/types.h>
#include <linux/aio_abi.h>
@@ -625,6 +626,9 @@ asmlinkage long sys_futex_wait(void __user *uaddr, unsigned int val,
struct __kernel_timespec __user *timo);
asmlinkage long sys_futex_wake(void __user *uaddr, unsigned int nr_wake,
unsigned int flags);
+asmlinkage long sys_futex_waitv(struct futex_waitv __user *waiters,
+ unsigned int nr_futexes, unsigned int flags,
+ struct __kernel_timespec __user *timo);
/* kernel/hrtimer.c */
asmlinkage long sys_nanosleep(struct __kernel_timespec __user *rqtp,
@@ -870,8 +870,11 @@ __SYSCALL(__NR_futex_wait, sys_futex_wait)
#define __NR_futex_wake 444
__SYSCALL(__NR_futex_wake, sys_futex_wake)
+#define __NR_futex_waitv 445
+__SC_COMP(__NR_futex_waitv, sys_futex_waitv, compat_sys_futex_waitv)
+
#undef __NR_syscalls
-#define __NR_syscalls 445
+#define __NR_syscalls 446
/*
* 32 bit systems traditionally used different
@@ -48,6 +48,20 @@
#define FUTEX_SHARED_FLAG 8
+#define FUTEX_WAITV_MAX 128
+
+/**
+ * struct futex_waitv - A waiter for vectorized wait
+ * @uaddr: User address to wait on
+ * @val: Expected value at uaddr
+ * @flags: Flags for this waiter
+ */
+struct futex_waitv {
+ void __user *uaddr;
+ unsigned int val;
+ unsigned int flags;
+};
+
/*
* Support for robust futexes: the kernel cleans up held futexes at
* thread exit time.
@@ -82,6 +82,12 @@ struct futex_bucket {
/* Mask for futex2 flag operations */
#define FUTEX2_MASK (FUTEX_SIZE_MASK | FUTEX_CLOCK_REALTIME | FUTEX_SHARED_FLAG)
+/* Mask for sys_futex_waitv flag */
+#define FUTEXV_MASK (FUTEX_CLOCK_REALTIME)
+
+/* Mask for each futex in futex_waitv list */
+#define FUTEXV_WAITER_MASK (FUTEX_SIZE_MASK | FUTEX_SHARED_FLAG)
+
#define is_object_shared ((futexv->objects[i].flags & FUTEX_SHARED_FLAG) ? true : false)
#define FUT_OFF_INODE 1 /* We set bit 0 if key has a reference on inode */
@@ -688,6 +694,177 @@ SYSCALL_DEFINE4(futex_wait, void __user *, uaddr, unsigned int, val,
return __futex_waitv(futexv, 1, timo, flags);
}
+#ifdef CONFIG_COMPAT
+/**
+ * compat_futex_parse_waitv - Parse a waitv array from userspace
+ * @futexv: Kernel side list of waiters to be filled
+ * @uwaitv: Userspace list to be parsed
+ * @nr_futexes: Length of futexv
+ *
+ * Return: Error code on failure, pointer to a prepared futexv otherwise
+ */
+static int compat_futex_parse_waitv(struct futex_waiter_head *futexv,
+ struct compat_futex_waitv __user *uwaitv,
+ unsigned int nr_futexes)
+{
+ struct futex_bucket *bucket;
+ struct compat_futex_waitv waitv;
+ unsigned int i;
+
+ for (i = 0; i < nr_futexes; i++) {
+ if (copy_from_user(&waitv, &uwaitv[i], sizeof(waitv)))
+ return -EFAULT;
+
+ if ((waitv.flags & ~FUTEXV_WAITER_MASK) ||
+ (waitv.flags & FUTEX_SIZE_MASK) != FUTEX_32)
+ return -EINVAL;
+
+ futexv->objects[i].key.pointer = 0;
+ futexv->objects[i].flags = waitv.flags;
+ futexv->objects[i].uaddr = compat_ptr(waitv.uaddr);
+ futexv->objects[i].val = waitv.val;
+ futexv->objects[i].index = i;
+
+ bucket = futex_get_bucket(compat_ptr(waitv.uaddr),
+ &futexv->objects[i].key,
+ is_object_shared);
+
+ if (IS_ERR(bucket))
+ return PTR_ERR(bucket);
+
+ futexv->objects[i].bucket = bucket;
+
+ INIT_LIST_HEAD(&futexv->objects[i].list);
+ }
+
+ return 0;
+}
+
+COMPAT_SYSCALL_DEFINE4(futex_waitv, struct compat_futex_waitv __user *, waiters,
+ unsigned int, nr_futexes, unsigned int, flags,
+ struct __kernel_timespec __user *, timo)
+{
+ struct futex_waiter_head *futexv;
+ int ret;
+
+ if (flags & ~FUTEXV_MASK)
+ return -EINVAL;
+
+ if (!nr_futexes || nr_futexes > FUTEX_WAITV_MAX || !waiters)
+ return -EINVAL;
+
+ futexv = kmalloc((sizeof(struct futex_waiter) * nr_futexes) +
+ sizeof(*futexv), GFP_KERNEL);
+ if (!futexv)
+ return -ENOMEM;
+
+ futexv->hint = false;
+ futexv->task = current;
+
+ ret = compat_futex_parse_waitv(futexv, waiters, nr_futexes);
+
+ if (!ret)
+ ret = __futex_waitv(futexv, nr_futexes, timo, flags);
+
+ kfree(futexv);
+
+ return ret;
+}
+#endif
+
+/**
+ * futex_parse_waitv - Parse a waitv array from userspace
+ * @futexv: Kernel side list of waiters to be filled
+ * @uwaitv: Userspace list to be parsed
+ * @nr_futexes: Length of futexv
+ *
+ * Return: Error code on failure, pointer to a prepared futexv otherwise
+ */
+static int futex_parse_waitv(struct futex_waiter_head *futexv,
+ struct futex_waitv __user *uwaitv,
+ unsigned int nr_futexes)
+{
+ struct futex_bucket *bucket;
+ struct futex_waitv waitv;
+ unsigned int i;
+
+ for (i = 0; i < nr_futexes; i++) {
+ if (copy_from_user(&waitv, &uwaitv[i], sizeof(waitv)))
+ return -EFAULT;
+
+ if ((waitv.flags & ~FUTEXV_WAITER_MASK) ||
+ (waitv.flags & FUTEX_SIZE_MASK) != FUTEX_32)
+ return -EINVAL;
+
+ futexv->objects[i].key.pointer = 0;
+ futexv->objects[i].flags = waitv.flags;
+ futexv->objects[i].uaddr = waitv.uaddr;
+ futexv->objects[i].val = waitv.val;
+ futexv->objects[i].index = i;
+
+ bucket = futex_get_bucket(waitv.uaddr, &futexv->objects[i].key,
+ is_object_shared);
+
+ if (IS_ERR(bucket))
+ return PTR_ERR(bucket);
+
+ futexv->objects[i].bucket = bucket;
+
+ INIT_LIST_HEAD(&futexv->objects[i].list);
+ }
+
+ return 0;
+}
+
+/**
+ * sys_futex_waitv - Wait on a list of futexes
+ * @waiters: List of futexes to wait on
+ * @nr_futexes: Length of futexv
+ * @flags: Flag for timeout (monotonic/realtime)
+ * @timo: Optional absolute timeout.
+ *
+ * Given an array of `struct futex_waitv`, wait on each uaddr. The thread wakes
+ * if a futex_wake() is performed at any uaddr. The syscall returns immediately
+ * if any waiter has *uaddr != val. *timo is an optional timeout value for the
+ * operation. Each waiter has individual flags. The `flags` argument for the
+ * syscall should be used solely for specifying the timeout as realtime, if
+ * needed. Flags for shared futexes, sizes, etc. should be used on the
+ * individual flags of each waiter.
+ *
+ * Returns the array index of one of the awaken futexes. There's no given
+ * information of how many were awakened, or any particular attribute of it (if
+ * it's the first awakened, if it is of the smaller index...).
+ */
+SYSCALL_DEFINE4(futex_waitv, struct futex_waitv __user *, waiters,
+ unsigned int, nr_futexes, unsigned int, flags,
+ struct __kernel_timespec __user *, timo)
+{
+ struct futex_waiter_head *futexv;
+ int ret;
+
+ if (flags & ~FUTEXV_MASK)
+ return -EINVAL;
+
+ if (!nr_futexes || nr_futexes > FUTEX_WAITV_MAX || !waiters)
+ return -EINVAL;
+
+ futexv = kmalloc((sizeof(struct futex_waiter) * nr_futexes) +
+ sizeof(*futexv), GFP_KERNEL);
+ if (!futexv)
+ return -ENOMEM;
+
+ futexv->hint = false;
+ futexv->task = current;
+
+ ret = futex_parse_waitv(futexv, waiters, nr_futexes);
+ if (!ret)
+ ret = __futex_waitv(futexv, nr_futexes, timo, flags);
+
+ kfree(futexv);
+
+ return ret;
+}
+
/**
* futex_get_parent - For a given futex in a futexv list, get a pointer to the futexv
* @waiter: Address of futex in the list
@@ -153,6 +153,7 @@ COND_SYSCALL_COMPAT(get_robust_list);
/* kernel/futex2.c */
COND_SYSCALL(futex_wait);
COND_SYSCALL(futex_wake);
+COND_SYSCALL(futex_waitv);
/* kernel/hrtimer.c */
@@ -870,8 +870,11 @@ __SYSCALL(__NR_futex_wait, sys_futex_wait)
#define __NR_futex_wake 444
__SYSCALL(__NR_futex_wake, sys_futex_wake)
+#define __NR_futex_waitv 445
+__SC_COMP(__NR_futex_waitv, sys_futex_waitv, compat_sys_futex_waitv)
+
#undef __NR_syscalls
-#define __NR_syscalls 445
+#define __NR_syscalls 446
/*
* 32 bit systems traditionally used different
@@ -365,6 +365,7 @@
441 common epoll_pwait2 sys_epoll_pwait2
443 common futex_wait sys_futex_wait
444 common futex_wake sys_futex_wake
+445 common futex_waitv sys_futex_waitv
#
# Due to a historical design error, certain syscalls are numbered differently