Commit cb8c4312 authored by peterz@infradead.org's avatar peterz@infradead.org Committed by Peter Zijlstra

futex: Add sys_futex_wait()

To complement sys_futex_waitv()/wake(), add sys_futex_wait(). This
syscall implements what was previously known as FUTEX_WAIT_BITSET
except it uses 'unsigned long' for the value and bitmask arguments,
takes timespec and clockid_t arguments for the absolute timeout and
uses FUTEX2 flags.

The 'unsigned long' allows FUTEX2_SIZE_U64 on 64bit platforms.
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarThomas Gleixner <tglx@linutronix.de>
Acked-by: default avatarGeert Uytterhoeven <geert@linux-m68k.org>
Link: https://lore.kernel.org/r/20230921105248.164324363@noisy.programming.kicks-ass.net
parent 43adf844
...@@ -493,3 +493,4 @@ ...@@ -493,3 +493,4 @@
561 common cachestat sys_cachestat 561 common cachestat sys_cachestat
562 common fchmodat2 sys_fchmodat2 562 common fchmodat2 sys_fchmodat2
563 common futex_wake sys_futex_wake 563 common futex_wake sys_futex_wake
564 common futex_wait sys_futex_wait
...@@ -467,3 +467,4 @@ ...@@ -467,3 +467,4 @@
451 common cachestat sys_cachestat 451 common cachestat sys_cachestat
452 common fchmodat2 sys_fchmodat2 452 common fchmodat2 sys_fchmodat2
454 common futex_wake sys_futex_wake 454 common futex_wake sys_futex_wake
455 common futex_wait sys_futex_wait
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
#define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5) #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5)
#define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800) #define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800)
#define __NR_compat_syscalls 455 #define __NR_compat_syscalls 456
#endif #endif
#define __ARCH_WANT_SYS_CLONE #define __ARCH_WANT_SYS_CLONE
......
...@@ -913,6 +913,8 @@ __SYSCALL(__NR_cachestat, sys_cachestat) ...@@ -913,6 +913,8 @@ __SYSCALL(__NR_cachestat, sys_cachestat)
__SYSCALL(__NR_fchmodat2, sys_fchmodat2) __SYSCALL(__NR_fchmodat2, sys_fchmodat2)
#define __NR_futex_wake 454 #define __NR_futex_wake 454
__SYSCALL(__NR_futex_wake, sys_futex_wake) __SYSCALL(__NR_futex_wake, sys_futex_wake)
#define __NR_futex_wait 455
__SYSCALL(__NR_futex_wait, sys_futex_wait)
/* /*
* Please add new compat syscalls above this comment and update * Please add new compat syscalls above this comment and update
......
...@@ -374,3 +374,4 @@ ...@@ -374,3 +374,4 @@
451 common cachestat sys_cachestat 451 common cachestat sys_cachestat
452 common fchmodat2 sys_fchmodat2 452 common fchmodat2 sys_fchmodat2
454 common futex_wake sys_futex_wake 454 common futex_wake sys_futex_wake
455 common futex_wait sys_futex_wait
...@@ -453,3 +453,4 @@ ...@@ -453,3 +453,4 @@
451 common cachestat sys_cachestat 451 common cachestat sys_cachestat
452 common fchmodat2 sys_fchmodat2 452 common fchmodat2 sys_fchmodat2
454 common futex_wake sys_futex_wake 454 common futex_wake sys_futex_wake
455 common futex_wait sys_futex_wait
...@@ -459,3 +459,4 @@ ...@@ -459,3 +459,4 @@
451 common cachestat sys_cachestat 451 common cachestat sys_cachestat
452 common fchmodat2 sys_fchmodat2 452 common fchmodat2 sys_fchmodat2
454 common futex_wake sys_futex_wake 454 common futex_wake sys_futex_wake
455 common futex_wait sys_futex_wait
...@@ -392,3 +392,4 @@ ...@@ -392,3 +392,4 @@
451 n32 cachestat sys_cachestat 451 n32 cachestat sys_cachestat
452 n32 fchmodat2 sys_fchmodat2 452 n32 fchmodat2 sys_fchmodat2
454 n32 futex_wake sys_futex_wake 454 n32 futex_wake sys_futex_wake
455 n32 futex_wait sys_futex_wait
...@@ -368,3 +368,4 @@ ...@@ -368,3 +368,4 @@
451 n64 cachestat sys_cachestat 451 n64 cachestat sys_cachestat
452 n64 fchmodat2 sys_fchmodat2 452 n64 fchmodat2 sys_fchmodat2
454 n64 futex_wake sys_futex_wake 454 n64 futex_wake sys_futex_wake
455 n64 futex_wait sys_futex_wait
...@@ -441,3 +441,4 @@ ...@@ -441,3 +441,4 @@
451 o32 cachestat sys_cachestat 451 o32 cachestat sys_cachestat
452 o32 fchmodat2 sys_fchmodat2 452 o32 fchmodat2 sys_fchmodat2
454 o32 futex_wake sys_futex_wake 454 o32 futex_wake sys_futex_wake
455 o32 futex_wait sys_futex_wait
...@@ -452,3 +452,4 @@ ...@@ -452,3 +452,4 @@
451 common cachestat sys_cachestat 451 common cachestat sys_cachestat
452 common fchmodat2 sys_fchmodat2 452 common fchmodat2 sys_fchmodat2
454 common futex_wake sys_futex_wake 454 common futex_wake sys_futex_wake
455 common futex_wait sys_futex_wait
...@@ -540,3 +540,4 @@ ...@@ -540,3 +540,4 @@
451 common cachestat sys_cachestat 451 common cachestat sys_cachestat
452 common fchmodat2 sys_fchmodat2 452 common fchmodat2 sys_fchmodat2
454 common futex_wake sys_futex_wake 454 common futex_wake sys_futex_wake
455 common futex_wait sys_futex_wait
...@@ -456,3 +456,4 @@ ...@@ -456,3 +456,4 @@
451 common cachestat sys_cachestat sys_cachestat 451 common cachestat sys_cachestat sys_cachestat
452 common fchmodat2 sys_fchmodat2 sys_fchmodat2 452 common fchmodat2 sys_fchmodat2 sys_fchmodat2
454 common futex_wake sys_futex_wake sys_futex_wake 454 common futex_wake sys_futex_wake sys_futex_wake
455 common futex_wait sys_futex_wait sys_futex_wait
...@@ -456,3 +456,4 @@ ...@@ -456,3 +456,4 @@
451 common cachestat sys_cachestat 451 common cachestat sys_cachestat
452 common fchmodat2 sys_fchmodat2 452 common fchmodat2 sys_fchmodat2
454 common futex_wake sys_futex_wake 454 common futex_wake sys_futex_wake
455 common futex_wait sys_futex_wait
...@@ -499,3 +499,4 @@ ...@@ -499,3 +499,4 @@
451 common cachestat sys_cachestat 451 common cachestat sys_cachestat
452 common fchmodat2 sys_fchmodat2 452 common fchmodat2 sys_fchmodat2
454 common futex_wake sys_futex_wake 454 common futex_wake sys_futex_wake
455 common futex_wait sys_futex_wait
...@@ -458,3 +458,4 @@ ...@@ -458,3 +458,4 @@
451 i386 cachestat sys_cachestat 451 i386 cachestat sys_cachestat
452 i386 fchmodat2 sys_fchmodat2 452 i386 fchmodat2 sys_fchmodat2
454 i386 futex_wake sys_futex_wake 454 i386 futex_wake sys_futex_wake
455 i386 futex_wait sys_futex_wait
...@@ -376,6 +376,7 @@ ...@@ -376,6 +376,7 @@
452 common fchmodat2 sys_fchmodat2 452 common fchmodat2 sys_fchmodat2
453 64 map_shadow_stack sys_map_shadow_stack 453 64 map_shadow_stack sys_map_shadow_stack
454 common futex_wake sys_futex_wake 454 common futex_wake sys_futex_wake
455 common futex_wait sys_futex_wait
# #
# Due to a historical design error, certain syscalls are numbered differently # Due to a historical design error, certain syscalls are numbered differently
......
...@@ -424,3 +424,4 @@ ...@@ -424,3 +424,4 @@
451 common cachestat sys_cachestat 451 common cachestat sys_cachestat
452 common fchmodat2 sys_fchmodat2 452 common fchmodat2 sys_fchmodat2
454 common futex_wake sys_futex_wake 454 common futex_wake sys_futex_wake
455 common futex_wait sys_futex_wait
...@@ -552,6 +552,10 @@ asmlinkage long sys_futex_waitv(struct futex_waitv *waiters, ...@@ -552,6 +552,10 @@ asmlinkage long sys_futex_waitv(struct futex_waitv *waiters,
asmlinkage long sys_futex_wake(void __user *uaddr, unsigned long mask, int nr, unsigned int flags); asmlinkage long sys_futex_wake(void __user *uaddr, unsigned long mask, int nr, unsigned int flags);
asmlinkage long sys_futex_wait(void __user *uaddr, unsigned long val, unsigned long mask,
unsigned int flags, struct __kernel_timespec __user *timespec,
clockid_t clockid);
asmlinkage long sys_nanosleep(struct __kernel_timespec __user *rqtp, asmlinkage long sys_nanosleep(struct __kernel_timespec __user *rqtp,
struct __kernel_timespec __user *rmtp); struct __kernel_timespec __user *rmtp);
asmlinkage long sys_nanosleep_time32(struct old_timespec32 __user *rqtp, asmlinkage long sys_nanosleep_time32(struct old_timespec32 __user *rqtp,
......
...@@ -824,9 +824,11 @@ __SYSCALL(__NR_cachestat, sys_cachestat) ...@@ -824,9 +824,11 @@ __SYSCALL(__NR_cachestat, sys_cachestat)
__SYSCALL(__NR_fchmodat2, sys_fchmodat2) __SYSCALL(__NR_fchmodat2, sys_fchmodat2)
#define __NR_futex_wake 454 #define __NR_futex_wake 454
__SYSCALL(__NR_futex_wake, sys_futex_wake) __SYSCALL(__NR_futex_wake, sys_futex_wake)
#define __NR_futex_wait 455
__SYSCALL(__NR_futex_wait, sys_futex_wait)
#undef __NR_syscalls #undef __NR_syscalls
#define __NR_syscalls 455 #define __NR_syscalls 456
/* /*
* 32 bit systems traditionally used different * 32 bit systems traditionally used different
......
...@@ -332,6 +332,9 @@ extern int futex_requeue(u32 __user *uaddr1, unsigned int flags, ...@@ -332,6 +332,9 @@ extern int futex_requeue(u32 __user *uaddr1, unsigned int flags,
u32 __user *uaddr2, int nr_wake, int nr_requeue, u32 __user *uaddr2, int nr_wake, int nr_requeue,
u32 *cmpval, int requeue_pi); u32 *cmpval, int requeue_pi);
extern int __futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
struct hrtimer_sleeper *to, u32 bitset);
extern int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, extern int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
ktime_t *abs_time, u32 bitset); ktime_t *abs_time, u32 bitset);
......
...@@ -221,6 +221,46 @@ static int futex_parse_waitv(struct futex_vector *futexv, ...@@ -221,6 +221,46 @@ static int futex_parse_waitv(struct futex_vector *futexv,
return 0; return 0;
} }
static int futex2_setup_timeout(struct __kernel_timespec __user *timeout,
clockid_t clockid, struct hrtimer_sleeper *to)
{
int flag_clkid = 0, flag_init = 0;
struct timespec64 ts;
ktime_t time;
int ret;
if (!timeout)
return 0;
if (clockid == CLOCK_REALTIME) {
flag_clkid = FLAGS_CLOCKRT;
flag_init = FUTEX_CLOCK_REALTIME;
}
if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
return -EINVAL;
if (get_timespec64(&ts, timeout))
return -EFAULT;
/*
* Since there's no opcode for futex_waitv, use
* FUTEX_WAIT_BITSET that uses absolute timeout as well
*/
ret = futex_init_timeout(FUTEX_WAIT_BITSET, flag_init, &ts, &time);
if (ret)
return ret;
futex_setup_timer(&time, to, flag_clkid, 0);
return 0;
}
static inline void futex2_destroy_timeout(struct hrtimer_sleeper *to)
{
hrtimer_cancel(&to->timer);
destroy_hrtimer_on_stack(&to->timer);
}
/** /**
* sys_futex_waitv - Wait on a list of futexes * sys_futex_waitv - Wait on a list of futexes
* @waiters: List of futexes to wait on * @waiters: List of futexes to wait on
...@@ -250,8 +290,6 @@ SYSCALL_DEFINE5(futex_waitv, struct futex_waitv __user *, waiters, ...@@ -250,8 +290,6 @@ SYSCALL_DEFINE5(futex_waitv, struct futex_waitv __user *, waiters,
{ {
struct hrtimer_sleeper to; struct hrtimer_sleeper to;
struct futex_vector *futexv; struct futex_vector *futexv;
struct timespec64 ts;
ktime_t time;
int ret; int ret;
/* This syscall supports no flags for now */ /* This syscall supports no flags for now */
...@@ -261,30 +299,8 @@ SYSCALL_DEFINE5(futex_waitv, struct futex_waitv __user *, waiters, ...@@ -261,30 +299,8 @@ SYSCALL_DEFINE5(futex_waitv, struct futex_waitv __user *, waiters,
if (!nr_futexes || nr_futexes > FUTEX_WAITV_MAX || !waiters) if (!nr_futexes || nr_futexes > FUTEX_WAITV_MAX || !waiters)
return -EINVAL; return -EINVAL;
if (timeout) { if (timeout && (ret = futex2_setup_timeout(timeout, clockid, &to)))
int flag_clkid = 0, flag_init = 0; return ret;
if (clockid == CLOCK_REALTIME) {
flag_clkid = FLAGS_CLOCKRT;
flag_init = FUTEX_CLOCK_REALTIME;
}
if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
return -EINVAL;
if (get_timespec64(&ts, timeout))
return -EFAULT;
/*
* Since there's no opcode for futex_waitv, use
* FUTEX_WAIT_BITSET that uses absolute timeout as well
*/
ret = futex_init_timeout(FUTEX_WAIT_BITSET, flag_init, &ts, &time);
if (ret)
return ret;
futex_setup_timer(&time, &to, flag_clkid, 0);
}
futexv = kcalloc(nr_futexes, sizeof(*futexv), GFP_KERNEL); futexv = kcalloc(nr_futexes, sizeof(*futexv), GFP_KERNEL);
if (!futexv) { if (!futexv) {
...@@ -299,10 +315,8 @@ SYSCALL_DEFINE5(futex_waitv, struct futex_waitv __user *, waiters, ...@@ -299,10 +315,8 @@ SYSCALL_DEFINE5(futex_waitv, struct futex_waitv __user *, waiters,
kfree(futexv); kfree(futexv);
destroy_timer: destroy_timer:
if (timeout) { if (timeout)
hrtimer_cancel(&to.timer); futex2_destroy_timeout(&to);
destroy_hrtimer_on_stack(&to.timer);
}
return ret; return ret;
} }
...@@ -336,6 +350,52 @@ SYSCALL_DEFINE4(futex_wake, ...@@ -336,6 +350,52 @@ SYSCALL_DEFINE4(futex_wake,
return futex_wake(uaddr, FLAGS_STRICT | flags, nr, mask); return futex_wake(uaddr, FLAGS_STRICT | flags, nr, mask);
} }
/*
* sys_futex_wait - Wait on a futex
* @uaddr: Address of the futex to wait on
* @val: Value of @uaddr
* @mask: bitmask
* @flags: FUTEX2 flags
* @timeout: Optional absolute timeout
* @clockid: Clock to be used for the timeout, realtime or monotonic
*
* Identical to the traditional FUTEX_WAIT_BITSET op, except it is part of the
* futex2 familiy of calls.
*/
SYSCALL_DEFINE6(futex_wait,
void __user *, uaddr,
unsigned long, val,
unsigned long, mask,
unsigned int, flags,
struct __kernel_timespec __user *, timeout,
clockid_t, clockid)
{
struct hrtimer_sleeper to;
int ret;
if (flags & ~FUTEX2_VALID_MASK)
return -EINVAL;
flags = futex2_to_flags(flags);
if (!futex_flags_valid(flags))
return -EINVAL;
if (!futex_validate_input(flags, val) ||
!futex_validate_input(flags, mask))
return -EINVAL;
if (timeout && (ret = futex2_setup_timeout(timeout, clockid, &to)))
return ret;
ret = __futex_wait(uaddr, flags, val, timeout ? &to : NULL, mask);
if (timeout)
futex2_destroy_timeout(&to);
return ret;
}
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
COMPAT_SYSCALL_DEFINE2(set_robust_list, COMPAT_SYSCALL_DEFINE2(set_robust_list,
struct compat_robust_list_head __user *, head, struct compat_robust_list_head __user *, head,
......
...@@ -632,20 +632,18 @@ int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags, ...@@ -632,20 +632,18 @@ int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
return ret; return ret;
} }
int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time, u32 bitset) int __futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
struct hrtimer_sleeper *to, u32 bitset)
{ {
struct hrtimer_sleeper timeout, *to;
struct restart_block *restart;
struct futex_hash_bucket *hb;
struct futex_q q = futex_q_init; struct futex_q q = futex_q_init;
struct futex_hash_bucket *hb;
int ret; int ret;
if (!bitset) if (!bitset)
return -EINVAL; return -EINVAL;
q.bitset = bitset; q.bitset = bitset;
to = futex_setup_timer(abs_time, &timeout, flags,
current->timer_slack_ns);
retry: retry:
/* /*
* Prepare to wait on uaddr. On success, it holds hb->lock and q * Prepare to wait on uaddr. On success, it holds hb->lock and q
...@@ -653,18 +651,17 @@ int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time ...@@ -653,18 +651,17 @@ int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time
*/ */
ret = futex_wait_setup(uaddr, val, flags, &q, &hb); ret = futex_wait_setup(uaddr, val, flags, &q, &hb);
if (ret) if (ret)
goto out; return ret;
/* futex_queue and wait for wakeup, timeout, or a signal. */ /* futex_queue and wait for wakeup, timeout, or a signal. */
futex_wait_queue(hb, &q, to); futex_wait_queue(hb, &q, to);
/* If we were woken (and unqueued), we succeeded, whatever. */ /* If we were woken (and unqueued), we succeeded, whatever. */
ret = 0;
if (!futex_unqueue(&q)) if (!futex_unqueue(&q))
goto out; return 0;
ret = -ETIMEDOUT;
if (to && !to->task) if (to && !to->task)
goto out; return -ETIMEDOUT;
/* /*
* We expect signal_pending(current), but we might be the * We expect signal_pending(current), but we might be the
...@@ -673,24 +670,38 @@ int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time ...@@ -673,24 +670,38 @@ int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time
if (!signal_pending(current)) if (!signal_pending(current))
goto retry; goto retry;
ret = -ERESTARTSYS; return -ERESTARTSYS;
if (!abs_time) }
goto out;
restart = &current->restart_block; int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time, u32 bitset)
restart->futex.uaddr = uaddr; {
restart->futex.val = val; struct hrtimer_sleeper timeout, *to;
restart->futex.time = *abs_time; struct restart_block *restart;
restart->futex.bitset = bitset; int ret;
restart->futex.flags = flags | FLAGS_HAS_TIMEOUT;
to = futex_setup_timer(abs_time, &timeout, flags,
current->timer_slack_ns);
ret = __futex_wait(uaddr, flags, val, to, bitset);
/* No timeout, nothing to clean up. */
if (!to)
return ret;
hrtimer_cancel(&to->timer);
destroy_hrtimer_on_stack(&to->timer);
ret = set_restart_fn(restart, futex_wait_restart); if (ret == -ERESTARTSYS) {
restart = &current->restart_block;
restart->futex.uaddr = uaddr;
restart->futex.val = val;
restart->futex.time = *abs_time;
restart->futex.bitset = bitset;
restart->futex.flags = flags | FLAGS_HAS_TIMEOUT;
out: return set_restart_fn(restart, futex_wait_restart);
if (to) {
hrtimer_cancel(&to->timer);
destroy_hrtimer_on_stack(&to->timer);
} }
return ret; return ret;
} }
......
...@@ -88,6 +88,7 @@ COND_SYSCALL(get_robust_list); ...@@ -88,6 +88,7 @@ COND_SYSCALL(get_robust_list);
COND_SYSCALL_COMPAT(get_robust_list); COND_SYSCALL_COMPAT(get_robust_list);
COND_SYSCALL(futex_waitv); COND_SYSCALL(futex_waitv);
COND_SYSCALL(futex_wake); COND_SYSCALL(futex_wake);
COND_SYSCALL(futex_wait);
COND_SYSCALL(kexec_load); COND_SYSCALL(kexec_load);
COND_SYSCALL_COMPAT(kexec_load); COND_SYSCALL_COMPAT(kexec_load);
COND_SYSCALL(init_module); COND_SYSCALL(init_module);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment