Skip to content

Commit cb8c431

Browse files
author
peterz@infradead.org
committed
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: Peter Zijlstra (Intel) <peterz@infradead.org> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Geert Uytterhoeven <geert@linux-m68k.org> Link: https://lore.kernel.org/r/20230921105248.164324363@noisy.programming.kicks-ass.net
1 parent 43adf84 commit cb8c431

File tree

24 files changed

+156
-57
lines changed

24 files changed

+156
-57
lines changed

arch/alpha/kernel/syscalls/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,3 +493,4 @@
493493
561 common cachestat sys_cachestat
494494
562 common fchmodat2 sys_fchmodat2
495495
563 common futex_wake sys_futex_wake
496+
564 common futex_wait sys_futex_wait

arch/arm/tools/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,3 +467,4 @@
467467
451 common cachestat sys_cachestat
468468
452 common fchmodat2 sys_fchmodat2
469469
454 common futex_wake sys_futex_wake
470+
455 common futex_wait sys_futex_wait

arch/arm64/include/asm/unistd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
#define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5)
4040
#define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800)
4141

42-
#define __NR_compat_syscalls 455
42+
#define __NR_compat_syscalls 456
4343
#endif
4444

4545
#define __ARCH_WANT_SYS_CLONE

arch/arm64/include/asm/unistd32.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,8 @@ __SYSCALL(__NR_cachestat, sys_cachestat)
913913
__SYSCALL(__NR_fchmodat2, sys_fchmodat2)
914914
#define __NR_futex_wake 454
915915
__SYSCALL(__NR_futex_wake, sys_futex_wake)
916+
#define __NR_futex_wait 455
917+
__SYSCALL(__NR_futex_wait, sys_futex_wait)
916918

917919
/*
918920
* Please add new compat syscalls above this comment and update

arch/ia64/kernel/syscalls/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,3 +374,4 @@
374374
451 common cachestat sys_cachestat
375375
452 common fchmodat2 sys_fchmodat2
376376
454 common futex_wake sys_futex_wake
377+
455 common futex_wait sys_futex_wait

arch/m68k/kernel/syscalls/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,3 +453,4 @@
453453
451 common cachestat sys_cachestat
454454
452 common fchmodat2 sys_fchmodat2
455455
454 common futex_wake sys_futex_wake
456+
455 common futex_wait sys_futex_wait

arch/microblaze/kernel/syscalls/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,3 +459,4 @@
459459
451 common cachestat sys_cachestat
460460
452 common fchmodat2 sys_fchmodat2
461461
454 common futex_wake sys_futex_wake
462+
455 common futex_wait sys_futex_wait

arch/mips/kernel/syscalls/syscall_n32.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,3 +392,4 @@
392392
451 n32 cachestat sys_cachestat
393393
452 n32 fchmodat2 sys_fchmodat2
394394
454 n32 futex_wake sys_futex_wake
395+
455 n32 futex_wait sys_futex_wait

arch/mips/kernel/syscalls/syscall_n64.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,3 +368,4 @@
368368
451 n64 cachestat sys_cachestat
369369
452 n64 fchmodat2 sys_fchmodat2
370370
454 n64 futex_wake sys_futex_wake
371+
455 n64 futex_wait sys_futex_wait

arch/mips/kernel/syscalls/syscall_o32.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,3 +441,4 @@
441441
451 o32 cachestat sys_cachestat
442442
452 o32 fchmodat2 sys_fchmodat2
443443
454 o32 futex_wake sys_futex_wake
444+
455 o32 futex_wait sys_futex_wait

arch/parisc/kernel/syscalls/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,4 @@
452452
451 common cachestat sys_cachestat
453453
452 common fchmodat2 sys_fchmodat2
454454
454 common futex_wake sys_futex_wake
455+
455 common futex_wait sys_futex_wait

arch/powerpc/kernel/syscalls/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,3 +540,4 @@
540540
451 common cachestat sys_cachestat
541541
452 common fchmodat2 sys_fchmodat2
542542
454 common futex_wake sys_futex_wake
543+
455 common futex_wait sys_futex_wait

arch/s390/kernel/syscalls/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,3 +456,4 @@
456456
451 common cachestat sys_cachestat sys_cachestat
457457
452 common fchmodat2 sys_fchmodat2 sys_fchmodat2
458458
454 common futex_wake sys_futex_wake sys_futex_wake
459+
455 common futex_wait sys_futex_wait sys_futex_wait

arch/sh/kernel/syscalls/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,3 +456,4 @@
456456
451 common cachestat sys_cachestat
457457
452 common fchmodat2 sys_fchmodat2
458458
454 common futex_wake sys_futex_wake
459+
455 common futex_wait sys_futex_wait

arch/sparc/kernel/syscalls/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,3 +499,4 @@
499499
451 common cachestat sys_cachestat
500500
452 common fchmodat2 sys_fchmodat2
501501
454 common futex_wake sys_futex_wake
502+
455 common futex_wait sys_futex_wait

arch/x86/entry/syscalls/syscall_32.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,3 +458,4 @@
458458
451 i386 cachestat sys_cachestat
459459
452 i386 fchmodat2 sys_fchmodat2
460460
454 i386 futex_wake sys_futex_wake
461+
455 i386 futex_wait sys_futex_wait

arch/x86/entry/syscalls/syscall_64.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@
376376
452 common fchmodat2 sys_fchmodat2
377377
453 64 map_shadow_stack sys_map_shadow_stack
378378
454 common futex_wake sys_futex_wake
379+
455 common futex_wait sys_futex_wait
379380

380381
#
381382
# Due to a historical design error, certain syscalls are numbered differently

arch/xtensa/kernel/syscalls/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,4 @@
424424
451 common cachestat sys_cachestat
425425
452 common fchmodat2 sys_fchmodat2
426426
454 common futex_wake sys_futex_wake
427+
455 common futex_wait sys_futex_wait

include/linux/syscalls.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,10 @@ asmlinkage long sys_futex_waitv(struct futex_waitv *waiters,
552552

553553
asmlinkage long sys_futex_wake(void __user *uaddr, unsigned long mask, int nr, unsigned int flags);
554554

555+
asmlinkage long sys_futex_wait(void __user *uaddr, unsigned long val, unsigned long mask,
556+
unsigned int flags, struct __kernel_timespec __user *timespec,
557+
clockid_t clockid);
558+
555559
asmlinkage long sys_nanosleep(struct __kernel_timespec __user *rqtp,
556560
struct __kernel_timespec __user *rmtp);
557561
asmlinkage long sys_nanosleep_time32(struct old_timespec32 __user *rqtp,

include/uapi/asm-generic/unistd.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,9 +824,11 @@ __SYSCALL(__NR_cachestat, sys_cachestat)
824824
__SYSCALL(__NR_fchmodat2, sys_fchmodat2)
825825
#define __NR_futex_wake 454
826826
__SYSCALL(__NR_futex_wake, sys_futex_wake)
827+
#define __NR_futex_wait 455
828+
__SYSCALL(__NR_futex_wait, sys_futex_wait)
827829

828830
#undef __NR_syscalls
829-
#define __NR_syscalls 455
831+
#define __NR_syscalls 456
830832

831833
/*
832834
* 32 bit systems traditionally used different

kernel/futex/futex.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,9 @@ extern int futex_requeue(u32 __user *uaddr1, unsigned int flags,
332332
u32 __user *uaddr2, int nr_wake, int nr_requeue,
333333
u32 *cmpval, int requeue_pi);
334334

335+
extern int __futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
336+
struct hrtimer_sleeper *to, u32 bitset);
337+
335338
extern int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
336339
ktime_t *abs_time, u32 bitset);
337340

kernel/futex/syscalls.c

Lines changed: 90 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,46 @@ static int futex_parse_waitv(struct futex_vector *futexv,
221221
return 0;
222222
}
223223

224+
static int futex2_setup_timeout(struct __kernel_timespec __user *timeout,
225+
clockid_t clockid, struct hrtimer_sleeper *to)
226+
{
227+
int flag_clkid = 0, flag_init = 0;
228+
struct timespec64 ts;
229+
ktime_t time;
230+
int ret;
231+
232+
if (!timeout)
233+
return 0;
234+
235+
if (clockid == CLOCK_REALTIME) {
236+
flag_clkid = FLAGS_CLOCKRT;
237+
flag_init = FUTEX_CLOCK_REALTIME;
238+
}
239+
240+
if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
241+
return -EINVAL;
242+
243+
if (get_timespec64(&ts, timeout))
244+
return -EFAULT;
245+
246+
/*
247+
* Since there's no opcode for futex_waitv, use
248+
* FUTEX_WAIT_BITSET that uses absolute timeout as well
249+
*/
250+
ret = futex_init_timeout(FUTEX_WAIT_BITSET, flag_init, &ts, &time);
251+
if (ret)
252+
return ret;
253+
254+
futex_setup_timer(&time, to, flag_clkid, 0);
255+
return 0;
256+
}
257+
258+
static inline void futex2_destroy_timeout(struct hrtimer_sleeper *to)
259+
{
260+
hrtimer_cancel(&to->timer);
261+
destroy_hrtimer_on_stack(&to->timer);
262+
}
263+
224264
/**
225265
* sys_futex_waitv - Wait on a list of futexes
226266
* @waiters: List of futexes to wait on
@@ -250,8 +290,6 @@ SYSCALL_DEFINE5(futex_waitv, struct futex_waitv __user *, waiters,
250290
{
251291
struct hrtimer_sleeper to;
252292
struct futex_vector *futexv;
253-
struct timespec64 ts;
254-
ktime_t time;
255293
int ret;
256294

257295
/* This syscall supports no flags for now */
@@ -261,30 +299,8 @@ SYSCALL_DEFINE5(futex_waitv, struct futex_waitv __user *, waiters,
261299
if (!nr_futexes || nr_futexes > FUTEX_WAITV_MAX || !waiters)
262300
return -EINVAL;
263301

264-
if (timeout) {
265-
int flag_clkid = 0, flag_init = 0;
266-
267-
if (clockid == CLOCK_REALTIME) {
268-
flag_clkid = FLAGS_CLOCKRT;
269-
flag_init = FUTEX_CLOCK_REALTIME;
270-
}
271-
272-
if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
273-
return -EINVAL;
274-
275-
if (get_timespec64(&ts, timeout))
276-
return -EFAULT;
277-
278-
/*
279-
* Since there's no opcode for futex_waitv, use
280-
* FUTEX_WAIT_BITSET that uses absolute timeout as well
281-
*/
282-
ret = futex_init_timeout(FUTEX_WAIT_BITSET, flag_init, &ts, &time);
283-
if (ret)
284-
return ret;
285-
286-
futex_setup_timer(&time, &to, flag_clkid, 0);
287-
}
302+
if (timeout && (ret = futex2_setup_timeout(timeout, clockid, &to)))
303+
return ret;
288304

289305
futexv = kcalloc(nr_futexes, sizeof(*futexv), GFP_KERNEL);
290306
if (!futexv) {
@@ -299,10 +315,8 @@ SYSCALL_DEFINE5(futex_waitv, struct futex_waitv __user *, waiters,
299315
kfree(futexv);
300316

301317
destroy_timer:
302-
if (timeout) {
303-
hrtimer_cancel(&to.timer);
304-
destroy_hrtimer_on_stack(&to.timer);
305-
}
318+
if (timeout)
319+
futex2_destroy_timeout(&to);
306320
return ret;
307321
}
308322

@@ -336,6 +350,52 @@ SYSCALL_DEFINE4(futex_wake,
336350
return futex_wake(uaddr, FLAGS_STRICT | flags, nr, mask);
337351
}
338352

353+
/*
354+
* sys_futex_wait - Wait on a futex
355+
* @uaddr: Address of the futex to wait on
356+
* @val: Value of @uaddr
357+
* @mask: bitmask
358+
* @flags: FUTEX2 flags
359+
* @timeout: Optional absolute timeout
360+
* @clockid: Clock to be used for the timeout, realtime or monotonic
361+
*
362+
* Identical to the traditional FUTEX_WAIT_BITSET op, except it is part of the
363+
* futex2 familiy of calls.
364+
*/
365+
366+
SYSCALL_DEFINE6(futex_wait,
367+
void __user *, uaddr,
368+
unsigned long, val,
369+
unsigned long, mask,
370+
unsigned int, flags,
371+
struct __kernel_timespec __user *, timeout,
372+
clockid_t, clockid)
373+
{
374+
struct hrtimer_sleeper to;
375+
int ret;
376+
377+
if (flags & ~FUTEX2_VALID_MASK)
378+
return -EINVAL;
379+
380+
flags = futex2_to_flags(flags);
381+
if (!futex_flags_valid(flags))
382+
return -EINVAL;
383+
384+
if (!futex_validate_input(flags, val) ||
385+
!futex_validate_input(flags, mask))
386+
return -EINVAL;
387+
388+
if (timeout && (ret = futex2_setup_timeout(timeout, clockid, &to)))
389+
return ret;
390+
391+
ret = __futex_wait(uaddr, flags, val, timeout ? &to : NULL, mask);
392+
393+
if (timeout)
394+
futex2_destroy_timeout(&to);
395+
396+
return ret;
397+
}
398+
339399
#ifdef CONFIG_COMPAT
340400
COMPAT_SYSCALL_DEFINE2(set_robust_list,
341401
struct compat_robust_list_head __user *, head,

kernel/futex/waitwake.c

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -632,39 +632,36 @@ int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
632632
return ret;
633633
}
634634

635-
int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time, u32 bitset)
635+
int __futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
636+
struct hrtimer_sleeper *to, u32 bitset)
636637
{
637-
struct hrtimer_sleeper timeout, *to;
638-
struct restart_block *restart;
639-
struct futex_hash_bucket *hb;
640638
struct futex_q q = futex_q_init;
639+
struct futex_hash_bucket *hb;
641640
int ret;
642641

643642
if (!bitset)
644643
return -EINVAL;
644+
645645
q.bitset = bitset;
646646

647-
to = futex_setup_timer(abs_time, &timeout, flags,
648-
current->timer_slack_ns);
649647
retry:
650648
/*
651649
* Prepare to wait on uaddr. On success, it holds hb->lock and q
652650
* is initialized.
653651
*/
654652
ret = futex_wait_setup(uaddr, val, flags, &q, &hb);
655653
if (ret)
656-
goto out;
654+
return ret;
657655

658656
/* futex_queue and wait for wakeup, timeout, or a signal. */
659657
futex_wait_queue(hb, &q, to);
660658

661659
/* If we were woken (and unqueued), we succeeded, whatever. */
662-
ret = 0;
663660
if (!futex_unqueue(&q))
664-
goto out;
665-
ret = -ETIMEDOUT;
661+
return 0;
662+
666663
if (to && !to->task)
667-
goto out;
664+
return -ETIMEDOUT;
668665

669666
/*
670667
* 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
673670
if (!signal_pending(current))
674671
goto retry;
675672

676-
ret = -ERESTARTSYS;
677-
if (!abs_time)
678-
goto out;
673+
return -ERESTARTSYS;
674+
}
679675

680-
restart = &current->restart_block;
681-
restart->futex.uaddr = uaddr;
682-
restart->futex.val = val;
683-
restart->futex.time = *abs_time;
684-
restart->futex.bitset = bitset;
685-
restart->futex.flags = flags | FLAGS_HAS_TIMEOUT;
676+
int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time, u32 bitset)
677+
{
678+
struct hrtimer_sleeper timeout, *to;
679+
struct restart_block *restart;
680+
int ret;
681+
682+
to = futex_setup_timer(abs_time, &timeout, flags,
683+
current->timer_slack_ns);
684+
685+
ret = __futex_wait(uaddr, flags, val, to, bitset);
686+
687+
/* No timeout, nothing to clean up. */
688+
if (!to)
689+
return ret;
690+
691+
hrtimer_cancel(&to->timer);
692+
destroy_hrtimer_on_stack(&to->timer);
686693

687-
ret = set_restart_fn(restart, futex_wait_restart);
694+
if (ret == -ERESTARTSYS) {
695+
restart = &current->restart_block;
696+
restart->futex.uaddr = uaddr;
697+
restart->futex.val = val;
698+
restart->futex.time = *abs_time;
699+
restart->futex.bitset = bitset;
700+
restart->futex.flags = flags | FLAGS_HAS_TIMEOUT;
688701

689-
out:
690-
if (to) {
691-
hrtimer_cancel(&to->timer);
692-
destroy_hrtimer_on_stack(&to->timer);
702+
return set_restart_fn(restart, futex_wait_restart);
693703
}
704+
694705
return ret;
695706
}
696707

kernel/sys_ni.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ COND_SYSCALL(get_robust_list);
8888
COND_SYSCALL_COMPAT(get_robust_list);
8989
COND_SYSCALL(futex_waitv);
9090
COND_SYSCALL(futex_wake);
91+
COND_SYSCALL(futex_wait);
9192
COND_SYSCALL(kexec_load);
9293
COND_SYSCALL_COMPAT(kexec_load);
9394
COND_SYSCALL(init_module);

0 commit comments

Comments
 (0)