summarylogtreecommitdiffstats
path: root/0001-futex.patch
diff options
context:
space:
mode:
Diffstat (limited to '0001-futex.patch')
-rw-r--r--0001-futex.patch1028
1 files changed, 0 insertions, 1028 deletions
diff --git a/0001-futex.patch b/0001-futex.patch
deleted file mode 100644
index 72bf55a8f28..00000000000
--- a/0001-futex.patch
+++ /dev/null
@@ -1,1028 +0,0 @@
-From 3c0db5afb3180941bf51dd7c12541514d774a250 Mon Sep 17 00:00:00 2001
-From: Gabriel Krisman Bertazi <krisman@collabora.com>
-Date: Thu, 13 Feb 2020 18:45:22 -0300
-Subject: [PATCH 1/4] futex: Implement mechanism to wait on any of several
- futexes
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This is a new futex operation, called FUTEX_WAIT_MULTIPLE, which allows
-a thread to wait on several futexes at the same time, and be awoken by
-any of them. In a sense, it implements one of the features that was
-supported by pooling on the old FUTEX_FD interface.
-
-The use case lies in the Wine implementation of the Windows NT interface
-WaitMultipleObjects. This Windows API function allows a thread to sleep
-waiting on the first of a set of event sources (mutexes, timers, signal,
-console input, etc) to signal. Considering this is a primitive
-synchronization operation for Windows applications, being able to quickly
-signal events on the producer side, and quickly go to sleep on the
-consumer side is essential for good performance of those running over Wine.
-
-Wine developers have an implementation that uses eventfd, but it suffers
-from FD exhaustion (there is applications that go to the order of
-multi-milion FDs), and higher CPU utilization than this new operation.
-
-The futex list is passed as an array of `struct futex_wait_block`
-(pointer, value, bitset) to the kernel, which will enqueue all of them
-and sleep if none was already triggered. It returns a hint of which
-futex caused the wake up event to userspace, but the hint doesn't
-guarantee that is the only futex triggered. Before calling the syscall
-again, userspace should traverse the list, trying to re-acquire any of
-the other futexes, to prevent an immediate -EWOULDBLOCK return code from
-the kernel.
-
-This was tested using three mechanisms:
-
-1) By reimplementing FUTEX_WAIT in terms of FUTEX_WAIT_MULTIPLE and
-running the unmodified tools/testing/selftests/futex and a full linux
-distro on top of this kernel.
-
-2) By an example code that exercises the FUTEX_WAIT_MULTIPLE path on a
-multi-threaded, event-handling setup.
-
-3) By running the Wine fsync with Valve's Proton compatibility code
-implementation and executing multi-threaded applications, in particular
-modern games, on top of this implementation.
-
-Changes were tested for the following ABIs: x86_64, i386 and x32.
-Support for x32 applications is not implemented since it would
-take a major rework adding a new entry point and splitting the current
-futex 64 entry point in two and we can't change the current x32 syscall
-number without breaking user space compatibility.
-
-CC: Steven Rostedt <rostedt@goodmis.org>
-Cc: Richard Yao <ryao@gentoo.org>
-Cc: Thomas Gleixner <tglx@linutronix.de>
-Cc: Peter Zijlstra <peterz@infradead.org>
-Co-developed-by: Zebediah Figura <z.figura12@gmail.com>
-Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
-Co-developed-by: Steven Noonan <steven@valvesoftware.com>
-Signed-off-by: Steven Noonan <steven@valvesoftware.com>
-Co-developed-by: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
-Signed-off-by: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
-Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
-[Added compatibility code]
-Co-developed-by: André Almeida <andrealmeid@collabora.com>
-Signed-off-by: André Almeida <andrealmeid@collabora.com>
----
- include/uapi/linux/futex.h | 20 ++
- kernel/futex.c | 363 ++++++++++++++++++++++++++++++++++++-
- 2 files changed, 379 insertions(+), 4 deletions(-)
-
-diff --git a/include/uapi/linux/futex.h b/include/uapi/linux/futex.h
-index a89eb0acc..a3e760886 100644
---- a/include/uapi/linux/futex.h
-+++ b/include/uapi/linux/futex.h
-@@ -21,6 +21,7 @@
- #define FUTEX_WAKE_BITSET 10
- #define FUTEX_WAIT_REQUEUE_PI 11
- #define FUTEX_CMP_REQUEUE_PI 12
-+#define FUTEX_WAIT_MULTIPLE 31
-
- #define FUTEX_PRIVATE_FLAG 128
- #define FUTEX_CLOCK_REALTIME 256
-@@ -40,6 +41,8 @@
- FUTEX_PRIVATE_FLAG)
- #define FUTEX_CMP_REQUEUE_PI_PRIVATE (FUTEX_CMP_REQUEUE_PI | \
- FUTEX_PRIVATE_FLAG)
-+#define FUTEX_WAIT_MULTIPLE_PRIVATE (FUTEX_WAIT_MULTIPLE | \
-+ FUTEX_PRIVATE_FLAG)
-
- /*
- * Support for robust futexes: the kernel cleans up held futexes at
-@@ -150,4 +153,21 @@ struct robust_list_head {
- (((op & 0xf) << 28) | ((cmp & 0xf) << 24) \
- | ((oparg & 0xfff) << 12) | (cmparg & 0xfff))
-
-+/*
-+ * Maximum number of multiple futexes to wait for
-+ */
-+#define FUTEX_MULTIPLE_MAX_COUNT 128
-+
-+/**
-+ * struct futex_wait_block - Block of futexes to be waited for
-+ * @uaddr: User address of the futex
-+ * @val: Futex value expected by userspace
-+ * @bitset: Bitset for the optional bitmasked wakeup
-+ */
-+struct futex_wait_block {
-+ __u32 __user *uaddr;
-+ __u32 val;
-+ __u32 bitset;
-+};
-+
- #endif /* _UAPI_LINUX_FUTEX_H */
-diff --git a/kernel/futex.c b/kernel/futex.c
-index b59532862..2fbfb0b80 100644
---- a/kernel/futex.c
-+++ b/kernel/futex.c
-@@ -214,6 +214,8 @@ struct futex_pi_state {
- * @rt_waiter: rt_waiter storage for use with requeue_pi
- * @requeue_pi_key: the requeue_pi target futex key
- * @bitset: bitset for the optional bitmasked wakeup
-+ * @uaddr: userspace address of futex
-+ * @uval: expected futex's value
- *
- * We use this hashed waitqueue, instead of a normal wait_queue_entry_t, so
- * we can wake only the relevant ones (hashed queues may be shared).
-@@ -236,6 +238,8 @@ struct futex_q {
- struct rt_mutex_waiter *rt_waiter;
- union futex_key *requeue_pi_key;
- u32 bitset;
-+ u32 __user *uaddr;
-+ u32 uval;
- } __randomize_layout;
-
- static const struct futex_q futex_q_init = {
-@@ -2346,6 +2350,29 @@ static int unqueue_me(struct futex_q *q)
- return ret;
- }
-
-+/**
-+ * unqueue_multiple() - Remove several futexes from their futex_hash_bucket
-+ * @q: The list of futexes to unqueue
-+ * @count: Number of futexes in the list
-+ *
-+ * Helper to unqueue a list of futexes. This can't fail.
-+ *
-+ * Return:
-+ * - >=0 - Index of the last futex that was awoken;
-+ * - -1 - If no futex was awoken
-+ */
-+static int unqueue_multiple(struct futex_q *q, int count)
-+{
-+ int ret = -1;
-+ int i;
-+
-+ for (i = 0; i < count; i++) {
-+ if (!unqueue_me(&q[i]))
-+ ret = i;
-+ }
-+ return ret;
-+}
-+
- /*
- * PI futexes can not be requeued and must remove themself from the
- * hash bucket. The hash bucket lock (i.e. lock_ptr) is held on entry
-@@ -2709,6 +2736,211 @@ static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
- return ret;
- }
-
-+/**
-+ * futex_wait_multiple_setup() - Prepare to wait and enqueue multiple futexes
-+ * @qs: The corresponding futex list
-+ * @count: The size of the lists
-+ * @flags: Futex flags (FLAGS_SHARED, etc.)
-+ * @awaken: Index of the last awoken futex
-+ *
-+ * Prepare multiple futexes in a single step and enqueue them. This may fail if
-+ * the futex list is invalid or if any futex was already awoken. On success the
-+ * task is ready to interruptible sleep.
-+ *
-+ * Return:
-+ * - 1 - One of the futexes was awaken by another thread
-+ * - 0 - Success
-+ * - <0 - -EFAULT, -EWOULDBLOCK or -EINVAL
-+ */
-+static int futex_wait_multiple_setup(struct futex_q *qs, int count,
-+ unsigned int flags, int *awaken)
-+{
-+ struct futex_hash_bucket *hb;
-+ int ret, i;
-+ u32 uval;
-+
-+ /*
-+ * Enqueuing multiple futexes is tricky, because we need to
-+ * enqueue each futex in the list before dealing with the next
-+ * one to avoid deadlocking on the hash bucket. But, before
-+ * enqueuing, we need to make sure that current->state is
-+ * TASK_INTERRUPTIBLE, so we don't absorb any awake events, which
-+ * cannot be done before the get_futex_key of the next key,
-+ * because it calls get_user_pages, which can sleep. Thus, we
-+ * fetch the list of futexes keys in two steps, by first pinning
-+ * all the memory keys in the futex key, and only then we read
-+ * each key and queue the corresponding futex.
-+ */
-+retry:
-+ for (i = 0; i < count; i++) {
-+ qs[i].key = FUTEX_KEY_INIT;
-+ ret = get_futex_key(qs[i].uaddr, flags & FLAGS_SHARED,
-+ &qs[i].key, FUTEX_READ);
-+ if (unlikely(ret)) {
-+ for (--i; i >= 0; i--)
-+ put_futex_key(&qs[i].key);
-+ return ret;
-+ }
-+ }
-+
-+ set_current_state(TASK_INTERRUPTIBLE);
-+
-+ for (i = 0; i < count; i++) {
-+ struct futex_q *q = &qs[i];
-+
-+ hb = queue_lock(q);
-+
-+ ret = get_futex_value_locked(&uval, q->uaddr);
-+ if (ret) {
-+ /*
-+ * We need to try to handle the fault, which
-+ * cannot be done without sleep, so we need to
-+ * undo all the work already done, to make sure
-+ * we don't miss any wake ups. Therefore, clean
-+ * up, handle the fault and retry from the
-+ * beginning.
-+ */
-+ queue_unlock(hb);
-+
-+ /*
-+ * Keys 0..(i-1) are implicitly put
-+ * on unqueue_multiple.
-+ */
-+ put_futex_key(&q->key);
-+
-+ *awaken = unqueue_multiple(qs, i);
-+
-+ __set_current_state(TASK_RUNNING);
-+
-+ /*
-+ * On a real fault, prioritize the error even if
-+ * some other futex was awoken. Userspace gave
-+ * us a bad address, -EFAULT them.
-+ */
-+ ret = get_user(uval, q->uaddr);
-+ if (ret)
-+ return ret;
-+
-+ /*
-+ * Even if the page fault was handled, If
-+ * something was already awaken, we can safely
-+ * give up and succeed to give a hint for userspace to
-+ * acquire the right futex faster.
-+ */
-+ if (*awaken >= 0)
-+ return 1;
-+
-+ goto retry;
-+ }
-+
-+ if (uval != q->uval) {
-+ queue_unlock(hb);
-+
-+ put_futex_key(&qs[i].key);
-+
-+ /*
-+ * If something was already awaken, we can
-+ * safely ignore the error and succeed.
-+ */
-+ *awaken = unqueue_multiple(qs, i);
-+ __set_current_state(TASK_RUNNING);
-+ if (*awaken >= 0)
-+ return 1;
-+
-+ return -EWOULDBLOCK;
-+ }
-+
-+ /*
-+ * The bucket lock can't be held while dealing with the
-+ * next futex. Queue each futex at this moment so hb can
-+ * be unlocked.
-+ */
-+ queue_me(&qs[i], hb);
-+ }
-+ return 0;
-+}
-+
-+/**
-+ * futex_wait_multiple() - Prepare to wait on and enqueue several futexes
-+ * @qs: The list of futexes to wait on
-+ * @op: Operation code from futex's syscall
-+ * @count: The number of objects
-+ * @abs_time: Timeout before giving up and returning to userspace
-+ *
-+ * Entry point for the FUTEX_WAIT_MULTIPLE futex operation, this function
-+ * sleeps on a group of futexes and returns on the first futex that
-+ * triggered, or after the timeout has elapsed.
-+ *
-+ * Return:
-+ * - >=0 - Hint to the futex that was awoken
-+ * - <0 - On error
-+ */
-+static int futex_wait_multiple(struct futex_q *qs, int op,
-+ u32 count, ktime_t *abs_time)
-+{
-+ struct hrtimer_sleeper timeout, *to;
-+ int ret, flags = 0, hint = 0;
-+ unsigned int i;
-+
-+ if (!(op & FUTEX_PRIVATE_FLAG))
-+ flags |= FLAGS_SHARED;
-+
-+ if (op & FUTEX_CLOCK_REALTIME)
-+ flags |= FLAGS_CLOCKRT;
-+
-+ to = futex_setup_timer(abs_time, &timeout, flags, 0);
-+ while (1) {
-+ ret = futex_wait_multiple_setup(qs, count, flags, &hint);
-+ if (ret) {
-+ if (ret > 0) {
-+ /* A futex was awaken during setup */
-+ ret = hint;
-+ }
-+ break;
-+ }
-+
-+ if (to)
-+ hrtimer_start_expires(&to->timer, HRTIMER_MODE_ABS);
-+
-+ /*
-+ * Avoid sleeping if another thread already tried to
-+ * wake us.
-+ */
-+ for (i = 0; i < count; i++) {
-+ if (plist_node_empty(&qs[i].list))
-+ break;
-+ }
-+
-+ if (i == count && (!to || to->task))
-+ freezable_schedule();
-+
-+ ret = unqueue_multiple(qs, count);
-+
-+ __set_current_state(TASK_RUNNING);
-+
-+ if (ret >= 0)
-+ break;
-+ if (to && !to->task) {
-+ ret = -ETIMEDOUT;
-+ break;
-+ } else if (signal_pending(current)) {
-+ ret = -ERESTARTSYS;
-+ break;
-+ }
-+ /*
-+ * The final case is a spurious wakeup, for
-+ * which just retry.
-+ */
-+ }
-+
-+ if (to) {
-+ hrtimer_cancel(&to->timer);
-+ destroy_hrtimer_on_stack(&to->timer);
-+ }
-+
-+ return ret;
-+}
-+
- static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
- ktime_t *abs_time, u32 bitset)
- {
-@@ -3833,6 +4065,43 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
- return -ENOSYS;
- }
-
-+/**
-+ * futex_read_wait_block - Read an array of futex_wait_block from userspace
-+ * @uaddr: Userspace address of the block
-+ * @count: Number of blocks to be read
-+ *
-+ * This function creates and allocate an array of futex_q (we zero it to
-+ * initialize the fields) and then, for each futex_wait_block element from
-+ * userspace, fill a futex_q element with proper values.
-+ */
-+inline struct futex_q *futex_read_wait_block(u32 __user *uaddr, u32 count)
-+{
-+ unsigned int i;
-+ struct futex_q *qs;
-+ struct futex_wait_block fwb;
-+ struct futex_wait_block __user *entry =
-+ (struct futex_wait_block __user *)uaddr;
-+
-+ if (!count || count > FUTEX_MULTIPLE_MAX_COUNT)
-+ return ERR_PTR(-EINVAL);
-+
-+ qs = kcalloc(count, sizeof(*qs), GFP_KERNEL);
-+ if (!qs)
-+ return ERR_PTR(-ENOMEM);
-+
-+ for (i = 0; i < count; i++) {
-+ if (copy_from_user(&fwb, &entry[i], sizeof(fwb))) {
-+ kfree(qs);
-+ return ERR_PTR(-EFAULT);
-+ }
-+
-+ qs[i].uaddr = fwb.uaddr;
-+ qs[i].uval = fwb.val;
-+ qs[i].bitset = fwb.bitset;
-+ }
-+
-+ return qs;
-+}
-
- SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
- struct __kernel_timespec __user *, utime, u32 __user *, uaddr2,
-@@ -3845,7 +4114,8 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
-
- if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
- cmd == FUTEX_WAIT_BITSET ||
-- cmd == FUTEX_WAIT_REQUEUE_PI)) {
-+ cmd == FUTEX_WAIT_REQUEUE_PI ||
-+ cmd == FUTEX_WAIT_MULTIPLE)) {
- if (unlikely(should_fail_futex(!(op & FUTEX_PRIVATE_FLAG))))
- return -EFAULT;
- if (get_timespec64(&ts, utime))
-@@ -3854,7 +4124,7 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
- return -EINVAL;
-
- t = timespec64_to_ktime(ts);
-- if (cmd == FUTEX_WAIT)
-+ if (cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_MULTIPLE)
- t = ktime_add_safe(ktime_get(), t);
- tp = &t;
- }
-@@ -3866,6 +4136,25 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
- cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
- val2 = (u32) (unsigned long) utime;
-
-+ if (cmd == FUTEX_WAIT_MULTIPLE) {
-+ int ret;
-+ struct futex_q *qs;
-+
-+#ifdef CONFIG_X86_X32
-+ if (unlikely(in_x32_syscall()))
-+ return -ENOSYS;
-+#endif
-+ qs = futex_read_wait_block(uaddr, val);
-+
-+ if (IS_ERR(qs))
-+ return PTR_ERR(qs);
-+
-+ ret = futex_wait_multiple(qs, op, val, tp);
-+ kfree(qs);
-+
-+ return ret;
-+ }
-+
- return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
- }
-
-@@ -4028,6 +4317,58 @@ COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid,
- #endif /* CONFIG_COMPAT */
-
- #ifdef CONFIG_COMPAT_32BIT_TIME
-+/**
-+ * struct compat_futex_wait_block - Block of futexes to be waited for
-+ * @uaddr: User address of the futex (compatible pointer)
-+ * @val: Futex value expected by userspace
-+ * @bitset: Bitset for the optional bitmasked wakeup
-+ */
-+struct compat_futex_wait_block {
-+ compat_uptr_t uaddr;
-+ __u32 pad;
-+ __u32 val;
-+ __u32 bitset;
-+};
-+
-+/**
-+ * compat_futex_read_wait_block - Read an array of futex_wait_block from
-+ * userspace
-+ * @uaddr: Userspace address of the block
-+ * @count: Number of blocks to be read
-+ *
-+ * This function does the same as futex_read_wait_block(), except that it
-+ * converts the pointer to the futex from the compat version to the regular one.
-+ */
-+inline struct futex_q *compat_futex_read_wait_block(u32 __user *uaddr,
-+ u32 count)
-+{
-+ unsigned int i;
-+ struct futex_q *qs;
-+ struct compat_futex_wait_block fwb;
-+ struct compat_futex_wait_block __user *entry =
-+ (struct compat_futex_wait_block __user *)uaddr;
-+
-+ if (!count || count > FUTEX_MULTIPLE_MAX_COUNT)
-+ return ERR_PTR(-EINVAL);
-+
-+ qs = kcalloc(count, sizeof(*qs), GFP_KERNEL);
-+ if (!qs)
-+ return ERR_PTR(-ENOMEM);
-+
-+ for (i = 0; i < count; i++) {
-+ if (copy_from_user(&fwb, &entry[i], sizeof(fwb))) {
-+ kfree(qs);
-+ return ERR_PTR(-EFAULT);
-+ }
-+
-+ qs[i].uaddr = compat_ptr(fwb.uaddr);
-+ qs[i].uval = fwb.val;
-+ qs[i].bitset = fwb.bitset;
-+ }
-+
-+ return qs;
-+}
-+
- SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val,
- struct old_timespec32 __user *, utime, u32 __user *, uaddr2,
- u32, val3)
-@@ -4039,14 +4380,15 @@ SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val,
-
- if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
- cmd == FUTEX_WAIT_BITSET ||
-- cmd == FUTEX_WAIT_REQUEUE_PI)) {
-+ cmd == FUTEX_WAIT_REQUEUE_PI ||
-+ cmd == FUTEX_WAIT_MULTIPLE)) {
- if (get_old_timespec32(&ts, utime))
- return -EFAULT;
- if (!timespec64_valid(&ts))
- return -EINVAL;
-
- t = timespec64_to_ktime(ts);
-- if (cmd == FUTEX_WAIT)
-+ if (cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_MULTIPLE)
- t = ktime_add_safe(ktime_get(), t);
- tp = &t;
- }
-@@ -4054,6 +4396,19 @@ SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val,
- cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
- val2 = (int) (unsigned long) utime;
-
-+ if (cmd == FUTEX_WAIT_MULTIPLE) {
-+ int ret;
-+ struct futex_q *qs = compat_futex_read_wait_block(uaddr, val);
-+
-+ if (IS_ERR(qs))
-+ return PTR_ERR(qs);
-+
-+ ret = futex_wait_multiple(qs, op, val, tp);
-+ kfree(qs);
-+
-+ return ret;
-+ }
-+
- return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
- }
- #endif /* CONFIG_COMPAT_32BIT_TIME */
---
-2.27.0.rc2
-
-
-From 3b2db8473486da199622782b28a9de69c4446323 Mon Sep 17 00:00:00 2001
-From: Gabriel Krisman Bertazi <krisman@collabora.com>
-Date: Thu, 13 Feb 2020 18:45:23 -0300
-Subject: [PATCH 2/4] selftests: futex: Add FUTEX_WAIT_MULTIPLE timeout test
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add test for timeout when waiting for multiple futexes. Skip the test if
-it's a x32 application and the kernel returned the approtiaded error,
-since this ABI is not supported for this operation.
-
-Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
-Co-developed-by: André Almeida <andrealmeid@collabora.com>
-Signed-off-by: André Almeida <andrealmeid@collabora.com>
----
- .../futex/functional/futex_wait_timeout.c | 38 ++++++++++++++++++-
- .../selftests/futex/include/futextest.h | 22 +++++++++++
- 2 files changed, 58 insertions(+), 2 deletions(-)
-
-diff --git a/tools/testing/selftests/futex/functional/futex_wait_timeout.c b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
-index ee55e6d38..2a63e1c2c 100644
---- a/tools/testing/selftests/futex/functional/futex_wait_timeout.c
-+++ b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
-@@ -11,6 +11,7 @@
- *
- * HISTORY
- * 2009-Nov-6: Initial version by Darren Hart <dvhart@linux.intel.com>
-+ * 2019-Dec-13: Add WAIT_MULTIPLE test by Krisman <krisman@collabora.com>
- *
- *****************************************************************************/
-
-@@ -41,6 +42,8 @@ int main(int argc, char *argv[])
- {
- futex_t f1 = FUTEX_INITIALIZER;
- struct timespec to;
-+ time_t secs;
-+ struct futex_wait_block fwb = {&f1, f1, 0};
- int res, ret = RET_PASS;
- int c;
-
-@@ -65,7 +68,7 @@ int main(int argc, char *argv[])
- }
-
- ksft_print_header();
-- ksft_set_plan(1);
-+ ksft_set_plan(2);
- ksft_print_msg("%s: Block on a futex and wait for timeout\n",
- basename(argv[0]));
- ksft_print_msg("\tArguments: timeout=%ldns\n", timeout_ns);
-@@ -79,8 +82,39 @@ int main(int argc, char *argv[])
- if (!res || errno != ETIMEDOUT) {
- fail("futex_wait returned %d\n", ret < 0 ? errno : ret);
- ret = RET_FAIL;
-+ } else
-+ ksft_test_result_pass("futex_wait timeout succeeds\n");
-+
-+ info("Calling futex_wait_multiple on f1: %u @ %p\n", f1, &f1);
-+
-+ /* Setup absolute time */
-+ ret = clock_gettime(CLOCK_REALTIME, &to);
-+ secs = (to.tv_nsec + timeout_ns) / 1000000000;
-+ to.tv_nsec = ((int64_t)to.tv_nsec + timeout_ns) % 1000000000;
-+ to.tv_sec += secs;
-+ info("to.tv_sec = %ld\n", to.tv_sec);
-+ info("to.tv_nsec = %ld\n", to.tv_nsec);
-+
-+ res = futex_wait_multiple(&fwb, 1, &to,
-+ FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME);
-+
-+#ifdef __ILP32__
-+ if (res == -1 && errno == ENOSYS) {
-+ ksft_test_result_skip("futex_wait_multiple not supported at x32\n");
-+ } else {
-+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
-+ res < 0 ? errno : res);
-+ ret = RET_FAIL;
- }
-+#else
-+ if (!res || errno != ETIMEDOUT) {
-+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
-+ res < 0 ? errno : res);
-+ ret = RET_FAIL;
-+ } else
-+ ksft_test_result_pass("futex_wait_multiple timeout succeeds\n");
-+#endif /* __ILP32__ */
-
-- print_result(TEST_NAME, ret);
-+ ksft_print_cnts();
- return ret;
- }
-diff --git a/tools/testing/selftests/futex/include/futextest.h b/tools/testing/selftests/futex/include/futextest.h
-index ddbcfc9b7..bb103bef4 100644
---- a/tools/testing/selftests/futex/include/futextest.h
-+++ b/tools/testing/selftests/futex/include/futextest.h
-@@ -38,6 +38,14 @@ typedef volatile u_int32_t futex_t;
- #ifndef FUTEX_CMP_REQUEUE_PI
- #define FUTEX_CMP_REQUEUE_PI 12
- #endif
-+#ifndef FUTEX_WAIT_MULTIPLE
-+#define FUTEX_WAIT_MULTIPLE 13
-+struct futex_wait_block {
-+ futex_t *uaddr;
-+ futex_t val;
-+ __u32 bitset;
-+};
-+#endif
- #ifndef FUTEX_WAIT_REQUEUE_PI_PRIVATE
- #define FUTEX_WAIT_REQUEUE_PI_PRIVATE (FUTEX_WAIT_REQUEUE_PI | \
- FUTEX_PRIVATE_FLAG)
-@@ -80,6 +88,20 @@ futex_wait(futex_t *uaddr, futex_t val, struct timespec *timeout, int opflags)
- return futex(uaddr, FUTEX_WAIT, val, timeout, NULL, 0, opflags);
- }
-
-+/**
-+ * futex_wait_multiple() - block on several futexes with optional timeout
-+ * @fwb: wait block user space address
-+ * @count: number of entities at fwb
-+ * @timeout: absolute timeout
-+ */
-+static inline int
-+futex_wait_multiple(struct futex_wait_block *fwb, int count,
-+ struct timespec *timeout, int opflags)
-+{
-+ return futex(fwb, FUTEX_WAIT_MULTIPLE, count, timeout, NULL, 0,
-+ opflags);
-+}
-+
- /**
- * futex_wake() - wake one or more tasks blocked on uaddr
- * @nr_wake: wake up to this many tasks
---
-2.27.0.rc2
-
-
-From b285216bc7b31eeba9b0e8081fe312bac7514931 Mon Sep 17 00:00:00 2001
-From: Gabriel Krisman Bertazi <krisman@collabora.com>
-Date: Thu, 13 Feb 2020 18:45:24 -0300
-Subject: [PATCH 3/4] selftests: futex: Add FUTEX_WAIT_MULTIPLE wouldblock test
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add test for wouldblock return when waiting for multiple futexes. Skip
-the test if it's a x32 application and the kernel returned the approtiaded
-error, since this ABI is not supported for this operation.
-
-Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
-Co-developed-by: André Almeida <andrealmeid@collabora.com>
-Signed-off-by: André Almeida <andrealmeid@collabora.com>
----
- .../futex/functional/futex_wait_wouldblock.c | 28 +++++++++++++++++--
- 1 file changed, 26 insertions(+), 2 deletions(-)
-
-diff --git a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
-index 0ae390ff8..bcbac0429 100644
---- a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
-+++ b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
-@@ -12,6 +12,7 @@
- *
- * HISTORY
- * 2009-Nov-14: Initial version by Gowrishankar <gowrishankar.m@in.ibm.com>
-+ * 2019-Dec-13: Add WAIT_MULTIPLE test by Krisman <krisman@collabora.com>
- *
- *****************************************************************************/
-
-@@ -40,6 +41,7 @@ int main(int argc, char *argv[])
- {
- struct timespec to = {.tv_sec = 0, .tv_nsec = timeout_ns};
- futex_t f1 = FUTEX_INITIALIZER;
-+ struct futex_wait_block fwb = {&f1, f1+1, 0};
- int res, ret = RET_PASS;
- int c;
-
-@@ -61,7 +63,7 @@ int main(int argc, char *argv[])
- }
-
- ksft_print_header();
-- ksft_set_plan(1);
-+ ksft_set_plan(2);
- ksft_print_msg("%s: Test the unexpected futex value in FUTEX_WAIT\n",
- basename(argv[0]));
-
-@@ -71,8 +73,30 @@ int main(int argc, char *argv[])
- fail("futex_wait returned: %d %s\n",
- res ? errno : res, res ? strerror(errno) : "");
- ret = RET_FAIL;
-+ } else
-+ ksft_test_result_pass("futex_wait wouldblock succeeds\n");
-+
-+ info("Calling futex_wait_multiple on f1: %u @ %p with val=%u\n",
-+ f1, &f1, f1+1);
-+ res = futex_wait_multiple(&fwb, 1, NULL, FUTEX_PRIVATE_FLAG);
-+
-+#ifdef __ILP32__
-+ if (res != -1 || errno != ENOSYS) {
-+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
-+ res < 0 ? errno : res);
-+ ret = RET_FAIL;
-+ } else {
-+ ksft_test_result_skip("futex_wait_multiple not supported at x32\n");
-+ }
-+#else
-+ if (!res || errno != EWOULDBLOCK) {
-+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
-+ res < 0 ? errno : res);
-+ ret = RET_FAIL;
- }
-+ ksft_test_result_pass("futex_wait_multiple wouldblock succeeds\n");
-+#endif /* __ILP32__ */
-
-- print_result(TEST_NAME, ret);
-+ ksft_print_cnts();
- return ret;
- }
---
-2.27.0.rc2
-
-
-From c2a6902699480877846a07e7567fbce94b370cce Mon Sep 17 00:00:00 2001
-From: Gabriel Krisman Bertazi <krisman@collabora.com>
-Date: Thu, 13 Feb 2020 18:45:25 -0300
-Subject: [PATCH 4/4] selftests: futex: Add FUTEX_WAIT_MULTIPLE wake up test
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add test for wait at multiple futexes mechanism. Skip the test if it's a
-x32 application and the kernel returned the approtiaded error, since this
-ABI is not supported for this operation.
-
-Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
-Co-developed-by: André Almeida <andrealmeid@collabora.com>
-Signed-off-by: André Almeida <andrealmeid@collabora.com>
----
- .../selftests/futex/functional/.gitignore | 1 +
- .../selftests/futex/functional/Makefile | 3 +-
- .../futex/functional/futex_wait_multiple.c | 173 ++++++++++++++++++
- .../testing/selftests/futex/functional/run.sh | 3 +
- 4 files changed, 179 insertions(+), 1 deletion(-)
- create mode 100644 tools/testing/selftests/futex/functional/futex_wait_multiple.c
-
-diff --git a/tools/testing/selftests/futex/functional/.gitignore b/tools/testing/selftests/futex/functional/.gitignore
-index 0efcd494d..03a4bedce 100644
---- a/tools/testing/selftests/futex/functional/.gitignore
-+++ b/tools/testing/selftests/futex/functional/.gitignore
-@@ -6,3 +6,4 @@ futex_wait_private_mapped_file
- futex_wait_timeout
- futex_wait_uninitialized_heap
- futex_wait_wouldblock
-+futex_wait_multiple
-diff --git a/tools/testing/selftests/futex/functional/Makefile b/tools/testing/selftests/futex/functional/Makefile
-index 23207829e..26562f2d7 100644
---- a/tools/testing/selftests/futex/functional/Makefile
-+++ b/tools/testing/selftests/futex/functional/Makefile
-@@ -14,7 +14,8 @@ TEST_GEN_FILES := \
- futex_requeue_pi_signal_restart \
- futex_requeue_pi_mismatched_ops \
- futex_wait_uninitialized_heap \
-- futex_wait_private_mapped_file
-+ futex_wait_private_mapped_file \
-+ futex_wait_multiple
-
- TEST_PROGS := run.sh
-
-diff --git a/tools/testing/selftests/futex/functional/futex_wait_multiple.c b/tools/testing/selftests/futex/functional/futex_wait_multiple.c
-new file mode 100644
-index 000000000..b48422e79
---- /dev/null
-+++ b/tools/testing/selftests/futex/functional/futex_wait_multiple.c
-@@ -0,0 +1,173 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+/******************************************************************************
-+ *
-+ * Copyright © Collabora, Ltd., 2019
-+ *
-+ * DESCRIPTION
-+ * Test basic semantics of FUTEX_WAIT_MULTIPLE
-+ *
-+ * AUTHOR
-+ * Gabriel Krisman Bertazi <krisman@collabora.com>
-+ *
-+ * HISTORY
-+ * 2019-Dec-13: Initial version by Krisman <krisman@collabora.com>
-+ *
-+ *****************************************************************************/
-+
-+#include <errno.h>
-+#include <getopt.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <time.h>
-+#include <pthread.h>
-+#include "futextest.h"
-+#include "logging.h"
-+
-+#define TEST_NAME "futex-wait-multiple"
-+#define timeout_ns 100000
-+#define MAX_COUNT 128
-+#define WAKE_WAIT_US 3000000
-+
-+int ret = RET_PASS;
-+char *progname;
-+futex_t f[MAX_COUNT] = {0};
-+struct futex_wait_block fwb[MAX_COUNT];
-+
-+void usage(char *prog)
-+{
-+ printf("Usage: %s\n", prog);
-+ printf(" -c Use color\n");
-+ printf(" -h Display this help message\n");
-+ printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
-+ VQUIET, VCRITICAL, VINFO);
-+}
-+
-+void test_count_overflow(void)
-+{
-+ futex_t f = FUTEX_INITIALIZER;
-+ struct futex_wait_block fwb[MAX_COUNT+1];
-+ int res, i;
-+
-+ ksft_print_msg("%s: Test a too big number of futexes\n", progname);
-+
-+ for (i = 0; i < MAX_COUNT+1; i++) {
-+ fwb[i].uaddr = &f;
-+ fwb[i].val = f;
-+ fwb[i].bitset = 0;
-+ }
-+
-+ res = futex_wait_multiple(fwb, MAX_COUNT+1, NULL, FUTEX_PRIVATE_FLAG);
-+
-+#ifdef __ILP32__
-+ if (res != -1 || errno != ENOSYS) {
-+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
-+ res < 0 ? errno : res);
-+ ret = RET_FAIL;
-+ } else {
-+ ksft_test_result_skip("futex_wait_multiple not supported at x32\n");
-+ }
-+#else
-+ if (res != -1 || errno != EINVAL) {
-+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
-+ res < 0 ? errno : res);
-+ ret = RET_FAIL;
-+ } else {
-+ ksft_test_result_pass("futex_wait_multiple count overflow succeed\n");
-+ }
-+
-+#endif /* __ILP32__ */
-+}
-+
-+void *waiterfn(void *arg)
-+{
-+ int res;
-+
-+ res = futex_wait_multiple(fwb, MAX_COUNT, NULL, FUTEX_PRIVATE_FLAG);
-+
-+#ifdef __ILP32__
-+ if (res != -1 || errno != ENOSYS) {
-+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
-+ res < 0 ? errno : res);
-+ ret = RET_FAIL;
-+ } else {
-+ ksft_test_result_skip("futex_wait_multiple not supported at x32\n");
-+ }
-+#else
-+ if (res < 0)
-+ ksft_print_msg("waiter failed %d\n", res);
-+
-+ info("futex_wait_multiple: Got hint futex %d was freed\n", res);
-+#endif /* __ILP32__ */
-+
-+ return NULL;
-+}
-+
-+void test_fwb_wakeup(void)
-+{
-+ int res, i;
-+ pthread_t waiter;
-+
-+ ksft_print_msg("%s: Test wake up in a list of futex\n", progname);
-+
-+ for (i = 0; i < MAX_COUNT; i++) {
-+ fwb[i].uaddr = &f[i];
-+ fwb[i].val = f[i];
-+ fwb[i].bitset = 0xffffffff;
-+ }
-+
-+ res = pthread_create(&waiter, NULL, waiterfn, NULL);
-+ if (res) {
-+ ksft_test_result_fail("Creating waiting thread failed");
-+ ksft_exit_fail();
-+ }
-+
-+ usleep(WAKE_WAIT_US);
-+ res = futex_wake(&(f[MAX_COUNT-1]), 1, FUTEX_PRIVATE_FLAG);
-+ if (res != 1) {
-+ ksft_test_result_fail("Failed to wake thread res=%d\n", res);
-+ ksft_exit_fail();
-+ }
-+
-+ pthread_join(waiter, NULL);
-+ ksft_test_result_pass("%s succeed\n", __func__);
-+}
-+
-+int main(int argc, char *argv[])
-+{
-+ int c;
-+
-+ while ((c = getopt(argc, argv, "cht:v:")) != -1) {
-+ switch (c) {
-+ case 'c':
-+ log_color(1);
-+ break;
-+ case 'h':
-+ usage(basename(argv[0]));
-+ exit(0);
-+ case 'v':
-+ log_verbosity(atoi(optarg));
-+ break;
-+ default:
-+ usage(basename(argv[0]));
-+ exit(1);
-+ }
-+ }
-+
-+ progname = basename(argv[0]);
-+
-+ ksft_print_header();
-+ ksft_set_plan(2);
-+
-+ test_count_overflow();
-+
-+#ifdef __ILP32__
-+ // if it's a 32x binary, there's no futex to wakeup
-+ ksft_test_result_skip("futex_wait_multiple not supported at x32\n");
-+#else
-+ test_fwb_wakeup();
-+#endif /* __ILP32__ */
-+
-+ ksft_print_cnts();
-+ return ret;
-+}
-diff --git a/tools/testing/selftests/futex/functional/run.sh b/tools/testing/selftests/futex/functional/run.sh
-index 1acb6ace1..a8be94f28 100755
---- a/tools/testing/selftests/futex/functional/run.sh
-+++ b/tools/testing/selftests/futex/functional/run.sh
-@@ -73,3 +73,6 @@ echo
- echo
- ./futex_wait_uninitialized_heap $COLOR
- ./futex_wait_private_mapped_file $COLOR
-+
-+echo
-+./futex_wait_multiple $COLOR
---
-2.27.0.rc2
-