summarylogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.SRCINFO22
-rw-r--r--PKGBUILD12
-rw-r--r--futex-Consolidate-duplicated-timer-setup-code.patch143
-rw-r--r--futex-wait-multiple-5.2.1.patch400
4 files changed, 564 insertions, 13 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 14e707cc27f0..bb8baf79dea9 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,6 +1,6 @@
# Generated by mksrcinfo v8
-# Tue Sep 10 20:42:33 UTC 2019
-pkgbase = linux
+# Tue Sep 10 20:43:37 UTC 2019
+pkgbase = linux-fsync
pkgver = 5.2.14.arch1
pkgrel = 1
url = https://git.archlinux.org/linux.git/log/?h=v5.2.14-arch1
@@ -22,25 +22,29 @@ pkgbase = linux
source = 60-linux.hook
source = 90-linux.hook
source = linux.preset
+ source = futex-wait-multiple-5.2.1.patch
+ source = futex-Consolidate-duplicated-timer-setup-code.patch
sha256sums = SKIP
sha256sums = e0d0f140128a8574217701e61e874a0a108f3b8cd0f6e35d8b16afe897999f8e
sha256sums = ae2e95db94ef7176207c690224169594d49445e04249d2499e9d2fbc117a0b21
sha256sums = c043f3033bb781e2688794a59f6d1f7ed49ef9b13eb77ff9a425df33a244a636
sha256sums = ad6344badc91ad0630caacde83f7f9b97276f80d26a20619a87952be65492c65
+ sha256sums = b8a9225b4b5cbabac26398d11cc26566e4407d150dacb92f3411c9bb8cc23942
+ sha256sums = 6aebfbe06e19207b80d06f382b5e0625958ff2db5719b98844d6de620c195be4
-pkgname = linux
- pkgdesc = The Linux kernel and modules
+pkgname = linux-fsync
+ pkgdesc = The Linux-fsync kernel and modules, including the futex-wait-multiple patchset for testing with Proton fsync
install = linux.install
depends = coreutils
depends = linux-firmware
depends = kmod
depends = mkinitcpio
optdepends = crda: to set the correct wireless channels of your country
- backup = etc/mkinitcpio.d/linux.preset
+ backup = etc/mkinitcpio.d/linux-fsync.preset
-pkgname = linux-headers
- pkgdesc = Header files and scripts for building modules for Linux kernel
+pkgname = linux-fsync-headers
+ pkgdesc = Header files and scripts for building modules for Linux-fsync kernel
-pkgname = linux-docs
- pkgdesc = Kernel hackers manual - HTML documentation that comes with the Linux kernel
+pkgname = linux-fsync-docs
+ pkgdesc = Kernel hackers manual - HTML documentation that comes with the Linux-fsync kernel
diff --git a/PKGBUILD b/PKGBUILD
index 31097733feb5..f63dd6b138e0 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -2,8 +2,8 @@
# Maintainer: Tobias Powalowski <tpowa@archlinux.org>
# Contributor: Thomas Baechler <thomas@archlinux.org>
-pkgbase=linux # Build stock -ARCH kernel
-#pkgbase=linux-custom # Build kernel with a different name
+#pkgbase=linux # Build stock -ARCH kernel
+pkgbase=linux-fsync # Build kernel with a different name
_srcver=5.2.14-arch1
pkgver=${_srcver//-/.}
pkgrel=1
@@ -22,6 +22,8 @@ source=(
60-linux.hook # pacman hook for depmod
90-linux.hook # pacman hook for initramfs regeneration
linux.preset # standard config files for mkinitcpio ramdisk
+ futex-wait-multiple-5.2.1.patch # futex-wait-multiple patchset w/opcode 31 for proton-4.11+ testing (aka fsync)
+ futex-Consolidate-duplicated-timer-setup-code.patch # 5.3 cleanup patch required for patchset
)
validpgpkeys=(
'ABAF11C65A2970B130ABE3C479BE3E4300411886' # Linus Torvalds
@@ -32,7 +34,9 @@ sha256sums=('SKIP'
'e0d0f140128a8574217701e61e874a0a108f3b8cd0f6e35d8b16afe897999f8e'
'ae2e95db94ef7176207c690224169594d49445e04249d2499e9d2fbc117a0b21'
'c043f3033bb781e2688794a59f6d1f7ed49ef9b13eb77ff9a425df33a244a636'
- 'ad6344badc91ad0630caacde83f7f9b97276f80d26a20619a87952be65492c65')
+ 'ad6344badc91ad0630caacde83f7f9b97276f80d26a20619a87952be65492c65'
+ 'b8a9225b4b5cbabac26398d11cc26566e4407d150dacb92f3411c9bb8cc23942'
+ '6aebfbe06e19207b80d06f382b5e0625958ff2db5719b98844d6de620c195be4')
_kernelname=${pkgbase#linux}
: ${_kernelname:=-ARCH}
@@ -68,7 +72,7 @@ build() {
}
_package() {
- pkgdesc="The ${pkgbase/linux/Linux} kernel and modules"
+ pkgdesc="The ${pkgbase/linux/Linux} kernel and modules, including the futex-wait-multiple patchset for testing with Proton fsync"
[[ $pkgbase = linux ]] && groups=(base)
depends=(coreutils linux-firmware kmod mkinitcpio)
optdepends=('crda: to set the correct wireless channels of your country')
diff --git a/futex-Consolidate-duplicated-timer-setup-code.patch b/futex-Consolidate-duplicated-timer-setup-code.patch
new file mode 100644
index 000000000000..473699fdd17b
--- /dev/null
+++ b/futex-Consolidate-duplicated-timer-setup-code.patch
@@ -0,0 +1,143 @@
+From 5ca584d935c32906d114924dc0e1dbfcbb13fdb2 Mon Sep 17 00:00:00 2001
+From: Waiman Long <longman@redhat.com>
+Date: Tue, 28 May 2019 12:03:45 -0400
+Subject: [PATCH] futex: Consolidate duplicated timer setup code
+
+Add a new futex_setup_timer() helper function to consolidate all the
+hrtimer_sleeper setup code.
+
+Signed-off-by: Waiman Long <longman@redhat.com>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Darren Hart <dvhart@infradead.org>
+Cc: Davidlohr Bueso <dave@stgolabs.net>
+Link: https://lkml.kernel.org/r/20190528160345.24017-1-longman@redhat.com
+---
+ kernel/futex.c | 69 ++++++++++++++++++++++++++++----------------------
+ 1 file changed, 39 insertions(+), 30 deletions(-)
+
+diff --git a/kernel/futex.c b/kernel/futex.c
+index 2268b97d5439..49bf20a8c512 100644
+--- a/kernel/futex.c
++++ b/kernel/futex.c
+@@ -483,6 +483,37 @@ enum futex_access {
+ FUTEX_WRITE
+ };
+
++/**
++ * futex_setup_timer - set up the sleeping hrtimer.
++ * @time: ptr to the given timeout value
++ * @timeout: the hrtimer_sleeper structure to be set up
++ * @flags: futex flags
++ * @range_ns: optional range in ns
++ *
++ * Return: Initialized hrtimer_sleeper structure or NULL if no timeout
++ * value given
++ */
++static inline struct hrtimer_sleeper *
++futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout,
++ int flags, u64 range_ns)
++{
++ if (!time)
++ return NULL;
++
++ hrtimer_init_on_stack(&timeout->timer, (flags & FLAGS_CLOCKRT) ?
++ CLOCK_REALTIME : CLOCK_MONOTONIC,
++ HRTIMER_MODE_ABS);
++ hrtimer_init_sleeper(timeout, current);
++
++ /*
++ * If range_ns is 0, calling hrtimer_set_expires_range_ns() is
++ * effectively the same as calling hrtimer_set_expires().
++ */
++ hrtimer_set_expires_range_ns(&timeout->timer, *time, range_ns);
++
++ return timeout;
++}
++
+ /**
+ * get_futex_key() - Get parameters which are the keys for a futex
+ * @uaddr: virtual address of the futex
+@@ -2692,7 +2723,7 @@ static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
+ static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
+ ktime_t *abs_time, u32 bitset)
+ {
+- struct hrtimer_sleeper timeout, *to = NULL;
++ struct hrtimer_sleeper timeout, *to;
+ struct restart_block *restart;
+ struct futex_hash_bucket *hb;
+ struct futex_q q = futex_q_init;
+@@ -2702,17 +2733,8 @@ static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
+ return -EINVAL;
+ q.bitset = bitset;
+
+- if (abs_time) {
+- to = &timeout;
+-
+- hrtimer_init_on_stack(&to->timer, (flags & FLAGS_CLOCKRT) ?
+- CLOCK_REALTIME : CLOCK_MONOTONIC,
+- HRTIMER_MODE_ABS);
+- hrtimer_init_sleeper(to, current);
+- hrtimer_set_expires_range_ns(&to->timer, *abs_time,
+- current->timer_slack_ns);
+- }
+-
++ to = futex_setup_timer(abs_time, &timeout, flags,
++ current->timer_slack_ns);
+ retry:
+ /*
+ * Prepare to wait on uaddr. On success, holds hb lock and increments
+@@ -2792,7 +2814,7 @@ static long futex_wait_restart(struct restart_block *restart)
+ static int futex_lock_pi(u32 __user *uaddr, unsigned int flags,
+ ktime_t *time, int trylock)
+ {
+- struct hrtimer_sleeper timeout, *to = NULL;
++ struct hrtimer_sleeper timeout, *to;
+ struct futex_pi_state *pi_state = NULL;
+ struct rt_mutex_waiter rt_waiter;
+ struct futex_hash_bucket *hb;
+@@ -2805,13 +2827,7 @@ static int futex_lock_pi(u32 __user *uaddr, unsigned int flags,
+ if (refill_pi_state_cache())
+ return -ENOMEM;
+
+- if (time) {
+- to = &timeout;
+- hrtimer_init_on_stack(&to->timer, CLOCK_REALTIME,
+- HRTIMER_MODE_ABS);
+- hrtimer_init_sleeper(to, current);
+- hrtimer_set_expires(&to->timer, *time);
+- }
++ to = futex_setup_timer(time, &timeout, FLAGS_CLOCKRT, 0);
+
+ retry:
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q.key, FUTEX_WRITE);
+@@ -3208,7 +3224,7 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
+ u32 val, ktime_t *abs_time, u32 bitset,
+ u32 __user *uaddr2)
+ {
+- struct hrtimer_sleeper timeout, *to = NULL;
++ struct hrtimer_sleeper timeout, *to;
+ struct futex_pi_state *pi_state = NULL;
+ struct rt_mutex_waiter rt_waiter;
+ struct futex_hash_bucket *hb;
+@@ -3225,15 +3241,8 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
+ if (!bitset)
+ return -EINVAL;
+
+- if (abs_time) {
+- to = &timeout;
+- hrtimer_init_on_stack(&to->timer, (flags & FLAGS_CLOCKRT) ?
+- CLOCK_REALTIME : CLOCK_MONOTONIC,
+- HRTIMER_MODE_ABS);
+- hrtimer_init_sleeper(to, current);
+- hrtimer_set_expires_range_ns(&to->timer, *abs_time,
+- current->timer_slack_ns);
+- }
++ to = futex_setup_timer(abs_time, &timeout, flags,
++ current->timer_slack_ns);
+
+ /*
+ * The waiter is allocated on our stack, manipulated by the requeue
+--
+2.22.0
+
diff --git a/futex-wait-multiple-5.2.1.patch b/futex-wait-multiple-5.2.1.patch
new file mode 100644
index 000000000000..d87407ded488
--- /dev/null
+++ b/futex-wait-multiple-5.2.1.patch
@@ -0,0 +1,400 @@
+From 18e885f4d416b8bcfa310f566956276dad5ce4d7 Mon Sep 17 00:00:00 2001
+From: John Schoenick <johns@valvesoftware.com>
+Date: Mon, 22 Jul 2019 17:06:17 -0700
+Subject: [PATCH] Squashed futex-wait-multiple patchset onto stable release
+ v5.2.1
+
+Includes opcode 31 patch for testing with proton-4.11+
+
+Squashed commit of the following:
+
+commit b0396eea1ad67904d96da55996b17cefc97b0a1c
+Author: Gabriel Krisman Bertazi <krisman@collabora.com>
+Date: Wed Jul 17 14:28:26 2019 -0400
+
+ futex: Change WAIT_MULTIPLE opcode to 31
+
+ Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
+
+commit bac93e6b84a9fd7114e6ec0ab6bca4d54fafc941
+Author: Gabriel Krisman Bertazi <krisman@collabora.com>
+Date: Mon Jul 8 09:44:09 2019 -0400
+
+ futex: Implement FUTEX_WAIT_MULTIPLE
+
+ This is a new futex operation to allow a thread to wait on several
+ futexes at the same time, and wake up on any of them. In a sense, it
+ implements one of the features that was supported by pooling on the old
+ FUTEX_FD interface.
+
+ My use case for this feature lies in Wine, where we want to implement a
+ similar function available in Windows, mainly for event handling. The
+ wine folks have an implementation of the userspace side using eventfd,
+ but it suffers from bad performance, as shown in the measurements below.
+
+ Technically, the old FUTEX_WAIT implementation can be easily
+ reimplemented using do_futex_wait_multiple, with a count one, and I have
+ a patch demonstrating how it works. I'm not proposing it, since futex
+ is such a tricky code, that I'd be more confortable to have
+ FUTEX_WAIT_MULTIPLE running upstream for a couple development cycles,
+ before considering modifying FUTEX_WAIT.
+
+ This was tested using three mechanisms:
+
+ 1) By reimplementing FUTEX_WAIT in terms of FUTEX_WAIT_MULTIPLE and
+ running 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 thread, event handling setup.
+
+ 3) By running the Wine fsync implementation and executing multi-threaded
+ applications, in particular modern games on top of the implementation.
+
+ Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
+ Signed-off-by: Steven Noonan <steven@valvesoftware.com>
+ Signed-off-by: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
+ Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
+
+commit f7817683081a4bb0dea6e8c0c090c9a4e35901bc
+Author: Gabriel Krisman Bertazi <krisman@collabora.com>
+Date: Fri Jul 12 14:16:20 2019 -0400
+
+ futex: Split key setup from key queue locking and read
+
+ split the futex key setup from the queue locking and key reading. This
+ is usefull to support the setup of multiple keys at the same time, like
+ what is done in futex_requeue() and what will be done for the
+ FUTEX_WAIT_MULTIPLE command.
+
+ Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
+---
+ include/uapi/linux/futex.h | 7 ++
+ kernel/futex.c | 214 ++++++++++++++++++++++++++++++++-----
+ 2 files changed, 196 insertions(+), 25 deletions(-)
+
+diff --git a/include/uapi/linux/futex.h b/include/uapi/linux/futex.h
+index a89eb0accd5e..c34e52e0f787 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
+@@ -150,4 +151,10 @@ struct robust_list_head {
+ (((op & 0xf) << 28) | ((cmp & 0xf) << 24) \
+ | ((oparg & 0xfff) << 12) | (cmparg & 0xfff))
+
++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 4b5b468c58b6..5c1c2e2c3aeb 100644
+--- a/kernel/futex.c
++++ b/kernel/futex.c
+@@ -183,6 +183,7 @@ static int __read_mostly futex_cmpxchg_enabled;
+ #endif
+ #define FLAGS_CLOCKRT 0x02
+ #define FLAGS_HAS_TIMEOUT 0x04
++#define FLAGS_WAKE_MULTIPLE 0x08
+
+ /*
+ * Priority Inheritance state:
+@@ -2600,6 +2601,39 @@ static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,
+ __set_current_state(TASK_RUNNING);
+ }
+
++static int __futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
++ struct futex_q *q, struct futex_hash_bucket **hb)
++{
++
++ u32 uval;
++ int ret;
++
++retry_private:
++ *hb = queue_lock(q);
++
++ ret = get_futex_value_locked(&uval, uaddr);
++
++ if (ret) {
++ queue_unlock(*hb);
++
++ ret = get_user(uval, uaddr);
++ if (ret)
++ return ret;
++
++ if (!(flags & FLAGS_SHARED))
++ goto retry_private;
++
++ return 1;
++ }
++
++ if (uval != val) {
++ queue_unlock(*hb);
++ ret = -EWOULDBLOCK;
++ }
++
++ return ret;
++}
++
+ /**
+ * futex_wait_setup() - Prepare to wait on a futex
+ * @uaddr: the futex userspace address
+@@ -2620,7 +2654,6 @@ static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,
+ static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
+ struct futex_q *q, struct futex_hash_bucket **hb)
+ {
+- u32 uval;
+ int ret;
+
+ /*
+@@ -2641,38 +2674,161 @@ static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
+ * absorb a wakeup if *uaddr does not match the desired values
+ * while the syscall executes.
+ */
+-retry:
+- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key, FUTEX_READ);
+- if (unlikely(ret != 0))
+- return ret;
++ do {
++ ret = get_futex_key(uaddr, flags & FLAGS_SHARED,
++ &q->key, FUTEX_READ);
++ if (unlikely(ret != 0))
++ return ret;
+
+-retry_private:
+- *hb = queue_lock(q);
++ ret = __futex_wait_setup(uaddr, val, flags, q, hb);
+
+- ret = get_futex_value_locked(&uval, uaddr);
+-
+- if (ret) {
+- queue_unlock(*hb);
+-
+- ret = get_user(uval, uaddr);
++ /* Drop key reference if retry or error. */
+ if (ret)
++ put_futex_key(&q->key);
++ } while (ret > 0);
++
++ return ret;
++}
++
++static int do_futex_wait_multiple(struct futex_wait_block *wb,
++ u32 count, unsigned int flags,
++ ktime_t *abs_time)
++{
++
++ struct hrtimer_sleeper timeout, *to;
++ struct futex_hash_bucket *hb;
++ struct futex_q *qs = NULL;
++ int ret;
++ int i;
++
++ qs = kcalloc(count, sizeof(struct futex_q), GFP_KERNEL);
++ if (!qs)
++ return -ENOMEM;
++
++ to = futex_setup_timer(abs_time, &timeout, flags,
++ current->timer_slack_ns);
++ retry:
++ for (i = 0; i < count; i++) {
++ qs[i].key = FUTEX_KEY_INIT;
++ qs[i].bitset = wb[i].bitset;
++
++ ret = get_futex_key(wb[i].uaddr, flags & FLAGS_SHARED,
++ &qs[i].key, FUTEX_READ);
++ if (unlikely(ret != 0)) {
++ for (--i; i >= 0; i--)
++ put_futex_key(&qs[i].key);
+ goto out;
++ }
++ }
++
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ for (i = 0; i < count; i++) {
++ ret = __futex_wait_setup(wb[i].uaddr, wb[i].val,
++ flags, &qs[i], &hb);
++ if (ret) {
++ /* Drop the failed key directly. keys 0..(i-1)
++ * will be put by unqueue_me. */
++ put_futex_key(&qs[i].key);
++
++ /* Undo the partial work we did. */
++ for (--i; i >= 0; i--)
++ unqueue_me(&qs[i]);
++
++ __set_current_state(TASK_RUNNING);
++ if (ret > 0)
++ goto retry;
++ goto out;
++ }
++
++ /* We can't hold to the bucket lock when dealing with
++ * the next futex. Queue ourselves now so we can unlock
++ * it before moving on. */
++ queue_me(&qs[i], hb);
++ }
++
++ if (to)
++ hrtimer_start_expires(&to->timer, HRTIMER_MODE_ABS);
++
++ /* There is no easy to way to check if we are wake already on
++ * multiple futexes without waking through each one of them. So
++ * just sleep and let the scheduler handle it.
++ */
++ if (!to || to->task)
++ freezable_schedule();
++
++ __set_current_state(TASK_RUNNING);
++
++ ret = -ETIMEDOUT;
++ /* If we were woken (and unqueued), we succeeded. */
++ for (i = 0; i < count; i++)
++ if (!unqueue_me(&qs[i]))
++ ret = i;
++
++ /* Succeed wakeup */
++ if (ret >= 0)
++ goto out;
+
+- if (!(flags & FLAGS_SHARED))
+- goto retry_private;
++ /* Woken by triggered timeout */
++ if (to && !to->task)
++ goto out;
+
+- put_futex_key(&q->key);
++ /*
++ * We expect signal_pending(current), but we might be the
++ * victim of a spurious wakeup as well.
++ */
++ if (!signal_pending(current))
+ goto retry;
++
++ ret = -ERESTARTSYS;
++ if (!abs_time)
++ goto out;
++
++ ret = -ERESTART_RESTARTBLOCK;
++ out:
++ if (to) {
++ hrtimer_cancel(&to->timer);
++ destroy_hrtimer_on_stack(&to->timer);
+ }
+
+- if (uval != val) {
+- queue_unlock(*hb);
+- ret = -EWOULDBLOCK;
++ kfree(qs);
++ return ret;
++}
++
++static int futex_wait_multiple(u32 __user *uaddr, unsigned int flags,
++ u32 count, ktime_t *abs_time)
++{
++ struct futex_wait_block *wb;
++ struct restart_block *restart;
++ int ret;
++
++ if (!count)
++ return -EINVAL;
++
++ wb = kcalloc(count, sizeof(struct futex_wait_block), GFP_KERNEL);
++ if (!wb)
++ return -ENOMEM;
++
++ if (copy_from_user(wb, uaddr,
++ count * sizeof(struct futex_wait_block))) {
++ ret = -EFAULT;
++ goto out;
++ }
++
++ ret = do_futex_wait_multiple(wb, count, flags, abs_time);
++
++ if (ret == -ERESTART_RESTARTBLOCK) {
++ restart = &current->restart_block;
++ restart->fn = futex_wait_restart;
++ restart->futex.uaddr = uaddr;
++ restart->futex.val = count;
++ restart->futex.time = *abs_time;
++ restart->futex.flags = (flags | FLAGS_HAS_TIMEOUT |
++ FLAGS_WAKE_MULTIPLE);
+ }
+
+ out:
+- if (ret)
+- put_futex_key(&q->key);
++ kfree(wb);
+ return ret;
+ }
+
+@@ -2762,6 +2918,10 @@ static long futex_wait_restart(struct restart_block *restart)
+ }
+ restart->fn = do_no_restart_syscall;
+
++ if (restart->futex.flags & FLAGS_WAKE_MULTIPLE)
++ return (long)futex_wait_multiple(uaddr, restart->futex.flags,
++ restart->futex.val, tp);
++
+ return (long)futex_wait(uaddr, restart->futex.flags,
+ restart->futex.val, tp, restart->futex.bitset);
+ }
+@@ -3658,6 +3818,8 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
+ uaddr2);
+ case FUTEX_CMP_REQUEUE_PI:
+ return futex_requeue(uaddr, flags, uaddr2, val, val2, &val3, 1);
++ case FUTEX_WAIT_MULTIPLE:
++ return futex_wait_multiple(uaddr, flags, val, timeout);
+ }
+ return -ENOSYS;
+ }
+@@ -3674,7 +3836,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))
+@@ -3683,7 +3846,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;
+ }
+@@ -3867,14 +4030,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;
+ }
+--
+2.22.0
+