[glibc/azanella/futex-wrappers] linux: Add futex_waitv

Adhemerval Zanella via Glibc-cvs <[email protected]>
Newsgroups gmane.comp.lib.glibc.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=f1f9d36769be8ebc43f642eb6a9bcdc97767047b

commit f1f9d36769be8ebc43f642eb6a9bcdc97767047b
Author: Adhemerval Zanella <[email protected]>
Date:   Wed Aug 19 15:55:53 2026 -0300

    linux: Add futex_waitv
    
    Add a wrapper for the futex_waitv system call to <sys/futex.h>:
    
      struct futex_waiter
      {
        uint64_t val;
        uint64_t uaddr;
        uint32_t flags;
        uint32_t __reserved;
      };
    
      int futex_waitv (const struct futex_waiter *waiters,
                       unsigned int nwaiters, unsigned int flags,
                       clockid_t clockid, const struct timespec *abstime);
    
    It blocks on up to FUTEX_WAITV_MAX (128) futexes at the same time
    until any of them is woken.  On success it returns the array index of
    the woken futex; on failure it returns -1 and sets errno.  The timeout
    is absolute, measured against CLOCK_REALTIME or CLOCK_MONOTONIC.
    
    The struct futex_waiter has the same layout as the kernel struct
    futex_waitv and each entry carries the kernel FUTEX2_* flags. The only
    extra handling is the negative absolute timeout, which the kernel
    rejects with EINVAL and the wrapper reports as ETIMEDOUT.
    
    The underlying system call only supports 64-bit time_t and so does the
    wrapper.
    
    Checked on x86_64-linux-gnu and i686-linux-gnu.

Diff:
---
 NEWS                                               |   4 +
 include/sys/futex.h                                |   5 +
 manual/threads.texi                                |  85 ++++++++-
 sysdeps/unix/sysv/linux/Makefile                   |  13 ++
 sysdeps/unix/sysv/linux/Versions                   |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist       |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist         |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist        |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist        |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist          |   1 +
 sysdeps/unix/sysv/linux/futex_waitv.c              |  38 ++++
 sysdeps/unix/sysv/linux/hppa/libc.abilist          |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist          |   1 +
 .../unix/sysv/linux/loongarch/ilp32/libc.abilist   |   1 +
 .../unix/sysv/linux/loongarch/lp64/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/microblaze/be/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/microblaze/le/libc.abilist |   1 +
 .../unix/sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../unix/sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../unix/sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../unix/sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist          |   1 +
 .../sysv/linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../linux/powerpc/powerpc32/nofpu/libc.abilist     |   1 +
 .../sysv/linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../sysv/linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/s390/libc.abilist          |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist         |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist         |   1 +
 sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sys/futex.h                |  50 ++++++
 sysdeps/unix/sysv/linux/tst-futex-consts.py        |  71 ++++++++
 sysdeps/unix/sysv/linux/tst-futex-waitv-time64.c   |   1 +
 sysdeps/unix/sysv/linux/tst-futex-waitv.c          | 197 +++++++++++++++++++++
 sysdeps/unix/sysv/linux/x86_64/64/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist    |   1 +
 42 files changed, 496 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 805a71b639..eec3907c71 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,10 @@ Major new features:
   the futex system call and provide the blocking building block for
   implementing custom synchronization primitives.
 
+* On Linux, the futex_waitv function has been added.  It allows waiting
+  on multiple futexes at the same time, reporting the index of the woken
+  one.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
   [Add deprecations, removals and changes affecting compatibility here]
diff --git a/include/sys/futex.h b/include/sys/futex.h
index 602096a738..ec19fcd042 100644
--- a/include/sys/futex.h
+++ b/include/sys/futex.h
@@ -21,6 +21,11 @@ extern int __futex_timedwait_time64 (uint32_t *futexp, uint32_t expected,
 				     unsigned int flags)
      __nonnull ((1));
 libc_hidden_proto (__futex_timedwait_time64)
+extern int futex_waitv (const struct futex_waiter *waiters,
+			unsigned int nwaiters, unsigned int flags,
+			const struct __timespec64 *abstime,
+			clockid_t clockid)
+     __nonnull ((1));
 # endif
 
 # endif /* !_ISOMAC */
diff --git a/manual/threads.texi b/manual/threads.texi
index 16c716f0ab..fffd1fabb9 100644
--- a/manual/threads.texi
+++ b/manual/threads.texi
@@ -1582,7 +1582,8 @@ the kernel to use faster process-local bookkeeping.  Without it, the
 futex word may be accessed by multiple processes through a shared
 memory mapping (@code{MAP_SHARED}).  Waiters and wakers of one futex
 word must agree on this flag.  The value follows the kernel
-@code{FUTEX_PRIVATE_FLAG}.
+@code{FUTEX_PRIVATE_FLAG}, so the same value is used by the
+@code{futex_waitv} per-waiter flags.
 
 @deftypefun int futex_wait (uint32_t *@var{futexp}, uint32_t @var{expected}, unsigned int @var{flags})
 @standards{Linux, sys/futex.h}
@@ -1675,6 +1676,88 @@ same address, @var{nwake} or @var{nrequeue} are negative, or
 @end table
 @end deftypefun
 
+@deftp {Data Type} {struct futex_waiter}
+@standards{Linux, sys/futex.h}
+Describes one futex to wait on in a call to @code{futex_waitv}.  It
+has the same layout as the kernel @code{struct futex_waitv} and the
+following members:
+
+@table @code
+@item uint64_t val
+The value the futex word is expected to contain, as for
+@code{futex_wait}.
+
+@item uint64_t uaddr
+The address of the futex word.
+
+@item uint32_t flags
+The kernel @code{FUTEX2_*} flags for this futex word:
+@code{FUTEX2_SIZE_U32}, optionally combined with @code{FUTEX2_PRIVATE}
+(equal to @code{FUTEX_PRIVATE_FLAG}) if the futex word is only used by
+threads of the calling process, @code{FUTEX2_NUMA} if the futex word
+is followed by a second 32-bit word holding the NUMA node id of the
+kernel state (with @code{FUTEX_NO_NODE} letting the kernel choose the
+node), and @code{FUTEX2_MPOL}.
+
+@item uint32_t __reserved
+Reserved for future use; must be zero.
+@end table
+@end deftp
+
+@deftypevr Macro int FUTEX_WAITV_MAX
+@standards{Linux, sys/futex.h}
+The maximum number of elements of the array passed to @code{futex_waitv},
+ currently 128.
+@end deftypevr
+
+@deftypefun int futex_waitv (const struct futex_waiter *@var{waiters}, unsigned int @var{nwaiters}, unsigned int @var{flags}, const struct timespec *@var{abstime}, clockid_t @var{clockid})
+@standards{Linux, sys/futex.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+Wait on all of the @var{nwaiters} futex words described by the array
+@var{waiters}, blocking until any one of them is woken (or until a
+spurious wake-up).  Each futex word is checked against the expected
+value recorded in its array entry, atomically with respect to
+concurrent futex operations, as for @code{futex_wait}.
+
+The wait is bounded by the absolute time @var{abstime} measured
+against the clock @var{clockid}, which must be @code{CLOCK_REALTIME}
+or @code{CLOCK_MONOTONIC}.  If @var{abstime} is a null pointer, the
+call blocks without a timeout.
+
+The @var{flags} argument is the flags argument of the underlying
+system call and is passed to the kernel unchanged; the system call
+currently supports no flags, so it must be zero.
+
+The return value is the array index of one of the woken futex words,
+or @minus{}1 with @code{errno} set to one of the following error
+codes.
+
+@table @code
+@item EAGAIN
+A futex word did not contain the value expected by its array entry at
+the time of the call.
+
+@item EINTR
+The wait was interrupted by a signal handler.
+
+@item ETIMEDOUT
+The absolute time @var{abstime} passed before any futex word was
+woken.
+
+@item EINVAL
+@var{nwaiters} is zero or greater than @code{FUTEX_WAITV_MAX}, a futex
+word is not aligned to four bytes, a @code{flags} member is not valid,
+or @var{clockid} is not a supported clock.
+
+@item ENOSYS
+The kernel does not support the @code{futex_waitv} system call, which
+was added in Linux 5.16.
+@end table
+
+This function only operates on 64-bit @code{time_t} and is only declared
+when @code{time_t} is a 64-bit type.
+@end deftypefun
+
 @c FIXME these are undocumented:
 @c pthread_atfork
 @c pthread_attr_destroy
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index cbe979f37a..12898e7049 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -77,6 +77,7 @@ sysdep_routines += \
   futex_requeue \
   futex_timedwait \
   futex_wait \
+  futex_waitv \
   futex_wake \
   fxstat \
   fxstat64 \
@@ -205,6 +206,7 @@ tests += \
   tst-fanotify \
   tst-fdopendir-o_path \
   tst-futex \
+  tst-futex-waitv \
   tst-getauxval \
   tst-gettid \
   tst-gettid-kill \
@@ -282,6 +284,7 @@ tests-time64 += \
   tst-clock_adjtime-time64 \
   tst-epoll-time64 \
   tst-futex-time64 \
+  tst-futex-waitv-time64 \
   tst-ntp_adjtime-time64 \
   tst-ntp_gettime-time64 \
   tst-ntp_gettimex-time64 \
@@ -390,6 +393,16 @@ $(objpfx)tst-pidfd-consts.out: ../sysdeps/unix/sysv/linux/tst-pidfd-consts.py
 	  < /dev/null > $@ 2>&1; $(evaluate-test)
 $(objpfx)tst-pidfd-consts.out: $(sysdeps-linux-python-deps)
 
+tests-special += \
+  $(objpfx)tst-futex-consts.out \
+  # tests-special
+$(objpfx)tst-futex-consts.out: ../sysdeps/unix/sysv/linux/tst-futex-consts.py
+	$(sysdeps-linux-python) \
+	  ../sysdeps/unix/sysv/linux/tst-futex-consts.py \
+	    $(sysdeps-linux-python-cc) \
+	  < /dev/null > $@ 2>&1; $(evaluate-test)
+$(objpfx)tst-futex-consts.out: $(sysdeps-linux-python-deps)
+
 tests-special += \
   $(objpfx)tst-mount-consts.out \
   # tests-special
diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
index b417759862..bd92f21d9d 100644
--- a/sysdeps/unix/sysv/linux/Versions
+++ b/sysdeps/unix/sysv/linux/Versions
@@ -350,6 +350,7 @@ libc {
 %endif
     futex_timedwait;
     futex_wait;
+    futex_waitv;
     futex_wake;
   }
   GLIBC_PRIVATE {
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 9ac3743c8d..738626576c 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2778,4 +2778,5 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 93781d00f1..53085aa5d0 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -3125,6 +3125,7 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index f081a512c2..3bde0417c9 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2539,4 +2539,5 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index 43b75f779a..1c9c1cdc26 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -2832,6 +2832,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index 0dd5e4948f..811a07e242 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -2829,6 +2829,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index c7675ff2ef..4280091906 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2816,4 +2816,5 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
diff --git a/sysdeps/unix/sysv/linux/futex_waitv.c b/sysdeps/unix/sysv/linux/futex_waitv.c
new file mode 100644
index 0000000000..5f0ca9057f
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/futex_waitv.c
@@ -0,0 +1,38 @@
+/* Wait on multiple futexes.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <time.h>
+#include <sys/futex.h>
+#include <sysdep.h>
+
+int
+futex_waitv (const struct futex_waiter *waiters, unsigned int nwaiters,
+	     unsigned int flags, const struct __timespec64 *abstime,
+	     clockid_t clockid)
+{
+  int r;
+  if (__glibc_unlikely (abstime != NULL && abstime->tv_sec < 0))
+    r = -ETIMEDOUT;
+  else
+    r = INTERNAL_SYSCALL_CALL (futex_waitv, waiters, nwaiters, flags,
+			       abstime, clockid);
+  return INTERNAL_SYSCALL_ERROR_P (r)
+         ? INLINE_SYSCALL_ERROR_RETURN_VALUE (-r)
+         : r;
+}
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 70957e8d70..07a11c8a05 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2853,6 +2853,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 0543cc2540..47d4ab3ee2 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -3036,6 +3036,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist
index 74b16c137f..4be7871d11 100644
--- a/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist
@@ -2290,4 +2290,5 @@ GLIBC_2.44 wscanf F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index 2962b730e8..c86486b76b 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2299,4 +2299,5 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 96c17703fd..4bd809f238 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -2812,6 +2812,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index a392bf3a3a..e0556e4a4f 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2979,6 +2979,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 1c419e0558..aa959e7d89 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2865,4 +2865,5 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 236b0a9949..1ffb895b90 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2862,4 +2862,5 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 59673b7709..40390d67c3 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2942,6 +2942,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 2cf11e9bd9..f0b9120c7f 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2940,6 +2940,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 12fcf6c276..fdd3b120a4 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2948,6 +2948,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index d2056aa028..cec2301f61 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2849,6 +2849,7 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index 7b82f1b2c8..98bdfd38d3 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2289,4 +2289,5 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 6ace1864a0..cbddfdb962 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -3169,6 +3169,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index 556cb13f78..fd3deb9994 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -3214,6 +3214,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index aede41f65e..3147bba3c0 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2922,6 +2922,7 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 60ee39b40c..c87ef1e840 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2998,4 +2998,5 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index 3760aa9c2e..3ad3deb6f4 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2542,4 +2542,5 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 4f19643514..bd10acb85b 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2742,4 +2742,5 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
diff --git a/sysdeps/unix/sysv/linux/s390/libc.abilist b/sysdeps/unix/sysv/linux/s390/libc.abilist
index 76d21db2a8..657bcfa87c 100644
--- a/sysdeps/unix/sysv/linux/s390/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/libc.abilist
@@ -2959,6 +2959,7 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index ff8093da80..5a74a6108b 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2859,6 +2859,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index acca32532b..1ad6937047 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2856,6 +2856,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index baa6d23a8e..21e4f5f13b 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -3190,6 +3190,7 @@ GLIBC_2.45 __futex_timedwait_time64 F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 4224217e0c..1877ed350f 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2825,6 +2825,7 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/sys/futex.h b/sysdeps/unix/sysv/linux/sys/futex.h
index 2830ae05fa..986869752f 100644
--- a/sysdeps/unix/sysv/linux/sys/futex.h
+++ b/sysdeps/unix/sysv/linux/sys/futex.h
@@ -29,6 +29,42 @@
 # define FUTEX_PRIVATE_FLAG	128
 #endif
 
+/* Maximum number of entries in the array passed to futex_waitv.  */
+#define FUTEX_WAITV_MAX		128
+
+/* Per-waiter flags for futex_waitv, following the kernel definitions
+   from <linux/futex.h>.  FUTEX2_PRIVATE equals FUTEX_PRIVATE_FLAG, so
+   the same value is used by all futex_* operations.  */
+#ifndef FUTEX2_SIZE_U32
+# define FUTEX2_SIZE_U32	0x02
+#endif
+#ifndef FUTEX2_PRIVATE
+# define FUTEX2_PRIVATE		FUTEX_PRIVATE_FLAG
+#endif
+/* With FUTEX2_NUMA the futex word is followed by a second 32-bit word
+   holding the NUMA node id of the kernel state associated with the
+   futex; FUTEX_NO_NODE there lets the kernel choose the node.
+   FUTEX2_MPOL applies the memory policy of the futex word address.  */
+#ifndef FUTEX2_NUMA
+# define FUTEX2_NUMA		0x04
+#endif
+#ifndef FUTEX2_MPOL
+# define FUTEX2_MPOL		0x08
+#endif
+#ifndef FUTEX_NO_NODE
+# define FUTEX_NO_NODE		(-1)
+#endif
+
+/* One futex to wait on in a futex_waitv call, with the same layout as
+   the kernel struct futex_waitv.  */
+struct futex_waiter
+{
+  uint64_t val;			/* Expected value of the futex word.  */
+  uint64_t uaddr;		/* Address of the futex word.  */
+  uint32_t flags;		/* FUTEX2_* flags for this futex word.  */
+  uint32_t __reserved;		/* Must be zero.  */
+};
+
 __BEGIN_DECLS
 
 /* If *FUTEXP == EXPECTED block until woken by futex_wake (or spuriously).
@@ -80,6 +116,20 @@ extern int futex_requeue (uint32_t *__futexp, uint32_t __expected,
 			  unsigned int __flags)
      __THROW __nonnull ((1, 4));
 
+/* Block on all of the NWAITERS futex words described by WAITERS (at most
+   FUTEX_WAITV_MAX entries) until woken on any of them or until the absolute
+   timeout ABSTIME measured against CLOCKID (which must be CLOCK_REALTIME or
+   CLOCK_MONOTONIC) passes.  If ABSTIME is null, block without a timeout.
+   Each entry checks its own expected value and carries its own FUTEX2_*
+   flags (FUTEX2_SIZE_U32, optionally with FUTEX2_PRIVATE).  */
+#ifdef __USE_TIME_BITS64
+extern int futex_waitv (const struct futex_waiter *__waiters,
+			unsigned int __nwaiters, unsigned int __flags,
+			const struct timespec *__abstime,
+			clockid_t __clockid)
+     __THROW __nonnull ((1));
+#endif
+
 __END_DECLS
 
 #endif /* sys/futex.h */
diff --git a/sysdeps/unix/sysv/linux/tst-futex-consts.py b/sysdeps/unix/sysv/linux/tst-futex-consts.py
new file mode 100644
index 0000000000..aef0cf3e1b
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-futex-consts.py
@@ -0,0 +1,71 @@
+#!/usr/bin/python3
+# Test that glibc's sys/futex.h constants match the kernel's.
+# Copyright (C) 2026 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+
+import argparse
+import sys
+
+import glibcextract
+import glibcsyscalls
+
+
+def main():
+    """The main entry point."""
+    parser = argparse.ArgumentParser(
+        description="Test that glibc's sys/futex.h constants "
+        "match the kernel's.")
+    parser.add_argument('--cc', metavar='CC',
+                        help='C compiler (including options) to use')
+    args = parser.parse_args()
+
+    if glibcextract.compile_c_snippet(
+            '#include <linux/futex.h>',
+            args.cc).returncode != 0:
+        sys.exit (77)
+
+    linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc)
+    # Constants in glibc were updated to match Linux v7.1.  When glibc
+    # constants are updated this value should be updated to match the
+    # released kernel version from which the constants were taken, and
+    # the futex_waitv per-waiter flags handling should be revisited for
+    # any new FUTEX2_* flag.
+    linux_version_glibc = (7, 1)
+    def check(cte, exclude=None):
+        return glibcextract.compare_macro_consts(
+                '#include <sys/futex.h>\n',
+                '#include <linux/futex.h>\n',
+                args.cc,
+                cte,
+                exclude,
+                linux_version_glibc > linux_version_headers,
+                linux_version_headers > linux_version_glibc)
+
+    status = max(
+        check('FUTEX_WAITV_MAX'),
+        check('FUTEX_PRIVATE_FLAG'),
+        check('FUTEX_NO_NODE'),
+        # glibc only defines the FUTEX2_* flags currently accepted by
+        # the futex_waitv system call; the sizes other than 32-bit and
+        # the size mask are not provided.
+        check('FUTEX2_.*',
+              'FUTEX2_SIZE_U8|FUTEX2_SIZE_U16|FUTEX2_SIZE_U64'
+              '|FUTEX2_SIZE_MASK'))
+    sys.exit(status)
+
+if __name__ == '__main__':
+    main()
diff --git a/sysdeps/unix/sysv/linux/tst-futex-waitv-time64.c b/sysdeps/unix/sysv/linux/tst-futex-waitv-time64.c
new file mode 100644
index 0000000000..1c1255bf17
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-futex-waitv-time64.c
@@ -0,0 +1 @@
+#include "tst-futex-waitv.c"
diff --git a/sysdeps/unix/sysv/linux/tst-futex-waitv.c b/sysdeps/unix/sysv/linux/tst-futex-waitv.c
new file mode 100644
index 0000000000..9dd14db86e
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-futex-waitv.c
@@ -0,0 +1,197 @@
+/* Test for futex_waitv.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <sys/futex.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <support/check.h>
+#include <support/test-driver.h>
+
+/* futex_waitv is only declared when time_t is a 64-bit type.  */
+#ifdef __USE_TIME_BITS64
+
+# include <support/support.h>
+# include <support/timespec.h>
+# include <support/xthread.h>
+# include <support/xtime.h>
+# include <support/xunistd.h>
+
+static uint32_t ftx1;
+static uint32_t ftx2;
+static int waiter_result;
+
+static void *
+waiter_tf (void *closure)
+{
+  struct futex_waiter waiters[] =
+    {
+      { .val = 0, .uaddr = (uintptr_t) &ftx1,
+	.flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE },
+      { .val = 0, .uaddr = (uintptr_t) &ftx2,
+	.flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE },
+    };
+  int r;
+  do
+    r = futex_waitv (waiters, 2, 0, NULL, CLOCK_MONOTONIC);
+  while (r == -1 && errno == EINTR);
+  TEST_VERIFY (r >= 0);
+  __atomic_store_n (&waiter_result, r + 1, __ATOMIC_RELEASE);
+  return NULL;
+}
+
+static int
+do_test_futex_writev (void)
+{
+  uint32_t word = 5;
+  struct futex_waiter waiter =
+    { .val = 4,
+      .uaddr = (uintptr_t) &word,
+      .flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE
+    };
+
+  /* Use a value mismatch to probe for kernel support (Linux 5.16).  */
+  {
+    int r = futex_waitv (&waiter, 1, 0, NULL, CLOCK_MONOTONIC);
+    TEST_COMPARE (r, -1);
+    if (errno == ENOSYS)
+      FAIL_UNSUPPORTED ("kernel does not support the futex_waitv syscall");
+    TEST_COMPARE (errno, EAGAIN);
+  }
+
+  /* Nonzero syscall flags are rejected by the kernel.  */
+  TEST_COMPARE (futex_waitv (&waiter, 1, ~0u, NULL, CLOCK_MONOTONIC), -1);
+  TEST_COMPARE (errno, EINVAL);
+
+  /* Invalid number of waiters and invalid per-waiter flags.  */
+  TEST_COMPARE (futex_waitv (&waiter, 0, 0, NULL, CLOCK_MONOTONIC), -1);
+  TEST_COMPARE (errno, EINVAL);
+  TEST_COMPARE (futex_waitv (&waiter, FUTEX_WAITV_MAX + 1, 0, NULL,
+			     CLOCK_MONOTONIC), -1);
+  TEST_COMPARE (errno, EINVAL);
+  {
+    struct futex_waiter bad =
+      { .val = 5, .uaddr = (uintptr_t) &word, .flags = ~0u };
+    TEST_COMPARE (futex_waitv (&bad, 1, 0, NULL, CLOCK_MONOTONIC), -1);
+    TEST_COMPARE (errno, EINVAL);
+  }
+
+  /* Invalid clock.  */
+  {
+    struct timespec ts = make_timespec (0, 0);
+    waiter.val = 5;
+    TEST_COMPARE (futex_waitv (&waiter, 1, 0, &ts, CLOCK_PROCESS_CPUTIME_ID),
+		  -1);
+    TEST_COMPARE (errno, EINVAL);
+  }
+
+  /* Timeouts in the past, including negative tv_sec.  */
+  {
+    struct timespec ts = make_timespec (0, 0);
+    TEST_COMPARE (futex_waitv (&waiter, 1, 0, &ts, CLOCK_MONOTONIC), -1);
+    TEST_COMPARE (errno, ETIMEDOUT);
+    ts = make_timespec (-1, 0);
+    TEST_COMPARE (futex_waitv (&waiter, 1, 0, &ts, CLOCK_REALTIME), -1);
+    TEST_COMPARE (errno, ETIMEDOUT);
+  }
+
+  /* An actual short wait on both supported clocks.  */
+  for (int i = 0; i < 2; i++)
+    {
+      clockid_t clockid = i == 0 ? CLOCK_REALTIME : CLOCK_MONOTONIC;
+      struct timespec timeout
+	= timespec_add (xclock_now (clockid), make_timespec (0, 100000000));
+      TEST_COMPARE (futex_waitv (&waiter, 1, 0, &timeout, clockid), -1);
+      TEST_COMPARE (errno, ETIMEDOUT);
+      TEST_TIMESPEC_NOW_OR_AFTER (clockid, timeout);
+    }
+
+  /* Block a thread on two futexes and wake it on the second one; the
+     return value reports the index of the woken futex.  */
+  {
+    pthread_t thr = xpthread_create (NULL, waiter_tf, NULL);
+
+    while (__atomic_load_n (&waiter_result, __ATOMIC_ACQUIRE) == 0)
+      futex_wake (&ftx2, INT_MAX, FUTEX2_PRIVATE);
+    TEST_COMPARE (__atomic_load_n (&waiter_result, __ATOMIC_ACQUIRE), 1 + 1);
+
+    xpthread_join (thr);
+  }
+
+  /* The same round-trip with process-shared futexes (no FUTEX2_PRIVATE)
+     across processes, with the futex words in a shared mapping.  */
+  {
+    uint32_t *sftx = support_shared_allocate (3 * sizeof (uint32_t));
+
+    pid_t pid = xfork ();
+    if (pid == 0)
+      {
+	struct futex_waiter waiters[] =
+	  {
+	    { .val = 0,
+	      .uaddr = (uintptr_t) &sftx[0],
+	      .flags = FUTEX2_SIZE_U32
+	    },
+	    { .val = 0,
+	      .uaddr = (uintptr_t) &sftx[1],
+	      .flags = FUTEX2_SIZE_U32
+	    },
+	  };
+	int r;
+	do
+	  r = futex_waitv (waiters, 2, 0, NULL, CLOCK_MONOTONIC);
+	while (r == -1 && errno == EINTR);
+	if (r < 0)
+	  _exit (2);
+	__atomic_store_n (&sftx[2], r + 1, __ATOMIC_RELEASE);
+	_exit (0);
+      }
+
+    /* Wake the child on the second shared futex.  */
+    while (__atomic_load_n (&sftx[2], __ATOMIC_ACQUIRE) == 0)
+      futex_wake (&sftx[1], INT_MAX, 0);
+    TEST_COMPARE (__atomic_load_n (&sftx[2], __ATOMIC_ACQUIRE), 1 + 1);
+
+    int status;
+    xwaitpid (pid, &status, 0);
+    TEST_VERIFY (WIFEXITED (status));
+    TEST_COMPARE (WEXITSTATUS (status), 0);
+
+    support_shared_free (sftx);
+  }
+
+  return 0;
+}
+
+#endif
+
+static int
+do_test (void)
+{
+#ifdef __USE_TIME_BITS64
+  return do_test_futex_writev ();
+#else
+  FAIL_UNSUPPORTED ("futex_waitv is only declared when time_t is a "
+		    "64-bit type (build with -D_TIME_BITS=64)");
+#endif
+}
+
+
+#include <support/test-driver.c>
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 8ae243e974..831583c13d 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2774,6 +2774,7 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
 GLIBC_2.5 __readlinkat_chk F
 GLIBC_2.5 inet6_opt_append F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index c9a00be076..ea5e23cf44 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2793,4 +2793,5 @@ GLIBC_2.43 umaxabs F
 GLIBC_2.45 futex_requeue F
 GLIBC_2.45 futex_timedwait F
 GLIBC_2.45 futex_wait F
+GLIBC_2.45 futex_waitv F
 GLIBC_2.45 futex_wake F
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.