@@ -454,3 +454,4 @@
447 i386 futex_wait sys_futex_wait compat_sys_futex_wait
448 i386 futex_wake sys_futex_wake
449 i386 futex_waitv sys_futex_waitv compat_sys_futex_waitv
+450 i386 futex_requeue sys_futex_requeue compat_sys_futex_requeue
@@ -371,6 +371,7 @@
447 common futex_wait sys_futex_wait
448 common futex_wake sys_futex_wake
449 common futex_waitv sys_futex_waitv
+450 common futex_requeue sys_futex_requeue
#
# Due to a historical design error, certain syscalls are numbered differently
@@ -374,6 +374,11 @@ struct compat_futex_waitv {
compat_uint_t flags;
};
+struct compat_futex_requeue {
+ compat_uptr_t uaddr;
+ compat_uint_t flags;
+};
+
#ifdef CONFIG_COMPAT_OLD_SIGACTION
struct compat_old_sigaction {
compat_uptr_t sa_handler;
@@ -705,6 +710,11 @@ asmlinkage long compat_sys_futex_wait(void __user *uaddr, compat_u64 val,
asmlinkage long compat_sys_futex_waitv(struct compat_futex_waitv *waiters,
compat_uint_t nr_futexes, compat_uint_t flags,
struct __kernel_timespec __user *timo);
+asmlinkage long compat_sys_futex_requeue(struct futex_requeue __user *rq1,
+ struct futex_requeue __user *rq2,
+ unsigned int nr_wake,
+ unsigned int nr_requeue,
+ u64 cmpval, unsigned int flags);
/* kernel/itimer.c */
asmlinkage long compat_sys_getitimer(int which,
struct old_itimerval32 __user *it);
@@ -159,6 +159,9 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
int futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset);
int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time,
u32 bitset);
+int futex_requeue(u32 __user *uaddr1, unsigned int flags1, u32 __user *uaddr2,
+ unsigned int flags2, int nr_wake, int nr_requeue, u32 *cmpval,
+ int requeue_pi);
void queue_me(struct futex_q *q, struct futex_hash_bucket *hb);
int unqueue_me(struct futex_q *q);
void queue_unlock(struct futex_hash_bucket *hb);
@@ -878,9 +878,11 @@ __SC_COMP(__NR_futex_wait, sys_futex_wait, compat_sys_futex_wait)
__SYSCALL(__NR_futex_wake, sys_futex_wake)
#define __NR_futex_waitv 449
__SC_COMP(__NR_futex_waitv, sys_futex_waitv, compat_sys_futex_waitv)
+#define __NR_futex_requeue 450
+__SC_COMP(__NR_futex_requeue, sys_futex_requeue, compat_sys_futex_requeue)
#undef __NR_syscalls
-#define __NR_syscalls 450
+#define __NR_syscalls 451
/*
* 32 bit systems traditionally used different
@@ -60,6 +60,16 @@ struct futex_waitv {
unsigned int flags;
};
+/**
+ * struct futex_requeue - Define an address and its flags for requeue operation
+ * @uaddr: User address of one of the requeue arguments
+ * @flags: Flags for this address
+ */
+struct futex_requeue {
+ void __user *uaddr;
+ unsigned int flags;
+};
+
/*
* Support for robust futexes: the kernel cleans up held futexes at
* thread exit time.
@@ -1843,9 +1843,9 @@ futex_proxy_trylock_atomic(u32 __user *pifutex, struct futex_hash_bucket *hb1,
* - >=0 - on success, the number of tasks requeued or woken;
* - <0 - on error
*/
-static int futex_requeue(u32 __user *uaddr1, unsigned int flags,
- u32 __user *uaddr2, int nr_wake, int nr_requeue,
- u32 *cmpval, int requeue_pi)
+int futex_requeue(u32 __user *uaddr1, unsigned int flags1, u32 __user *uaddr2,
+ unsigned int flags2, int nr_wake, int nr_requeue,
+ u32 *cmpval, int requeue_pi)
{
union futex_key key1 = FUTEX_KEY_INIT, key2 = FUTEX_KEY_INIT;
int task_count = 0, ret;
@@ -1895,10 +1895,10 @@ static int futex_requeue(u32 __user *uaddr1, unsigned int flags,
}
retry:
- ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, FUTEX_READ);
+ ret = get_futex_key(uaddr1, flags1 & FLAGS_SHARED, &key1, FUTEX_READ);
if (unlikely(ret != 0))
return ret;
- ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2,
+ ret = get_futex_key(uaddr2, flags2 & FLAGS_SHARED, &key2,
requeue_pi ? FUTEX_WRITE : FUTEX_READ);
if (unlikely(ret != 0))
return ret;
@@ -1930,7 +1930,7 @@ static int futex_requeue(u32 __user *uaddr1, unsigned int flags,
if (ret)
return ret;
- if (!(flags & FLAGS_SHARED))
+ if (!(flags1 & FLAGS_SHARED))
goto retry_private;
goto retry;
@@ -3661,9 +3661,9 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
case FUTEX_WAKE_BITSET:
return futex_wake(uaddr, flags, val, val3);
case FUTEX_REQUEUE:
- return futex_requeue(uaddr, flags, uaddr2, val, val2, NULL, 0);
+ return futex_requeue(uaddr, flags, uaddr2, flags, val, val2, NULL, 0);
case FUTEX_CMP_REQUEUE:
- return futex_requeue(uaddr, flags, uaddr2, val, val2, &val3, 0);
+ return futex_requeue(uaddr, flags, uaddr2, flags, val, val2, &val3, 0);
case FUTEX_WAKE_OP:
return futex_wake_op(uaddr, flags, uaddr2, val, val2, val3);
case FUTEX_LOCK_PI:
@@ -3680,7 +3680,7 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
return futex_wait_requeue_pi(uaddr, flags, val, timeout, val3,
uaddr2);
case FUTEX_CMP_REQUEUE_PI:
- return futex_requeue(uaddr, flags, uaddr2, val, val2, &val3, 1);
+ return futex_requeue(uaddr, flags, uaddr2, flags, val, val2, &val3, 1);
}
return -ENOSYS;
}
@@ -446,3 +446,114 @@ SYSCALL_DEFINE3(futex_wake, void __user *, uaddr, unsigned int, nr_wake,
return futex_wake(uaddr, futex_flags, nr_wake, FUTEX_BITSET_MATCH_ANY);
}
+
+#ifdef CONFIG_COMPAT
+static int compat_futex_parse_requeue(struct compat_futex_requeue __user *rq,
+ void __user **uaddr, unsigned int *flags)
+{
+ struct compat_futex_requeue aux;
+ unsigned int futex_flags = 0;
+
+ if (copy_from_user(&aux, rq, sizeof(*rq)))
+ return -EFAULT;
+
+ if (aux.flags & ~FUTEXV_WAITER_MASK ||
+ (aux.flags & FUTEX_SIZE_MASK) != FUTEX_32)
+ return -EINVAL;
+
+ if (aux.flags & FUTEX_SHARED_FLAG)
+ futex_flags |= FLAGS_SHARED;
+
+ *uaddr = compat_ptr(aux.uaddr);
+ *flags = futex_flags;
+
+ return 0;
+}
+
+COMPAT_SYSCALL_DEFINE6(futex_requeue, struct compat_futex_requeue __user *, rq1,
+ struct compat_futex_requeue __user *, rq2,
+ unsigned int, nr_wake, unsigned int, nr_requeue,
+ compat_u64, cmpval, unsigned int, flags)
+{
+ void __user *uaddr1, *uaddr2;
+ unsigned int flags1, flags2;
+ u32 val = cmpval;
+ int ret;
+
+ if (flags)
+ return -EINVAL;
+
+ ret = compat_futex_parse_requeue(rq1, &uaddr1, &flags1);
+ if (ret)
+ return ret;
+
+ ret = compat_futex_parse_requeue(rq2, &uaddr2, &flags2);
+ if (ret)
+ return ret;
+
+ return futex_requeue(uaddr1, flags1, uaddr2, flags2, nr_wake, nr_requeue, &val, 0);
+}
+#endif
+
+static int futex_parse_requeue(struct futex_requeue __user *rq,
+ void __user **uaddr, unsigned int *flags)
+{
+ struct futex_requeue aux;
+ unsigned int futex_flags = 0;
+
+ if (copy_from_user(&aux, rq, sizeof(*rq)))
+ return -EFAULT;
+
+ if (aux.flags & ~FUTEXV_WAITER_MASK ||
+ (aux.flags & FUTEX_SIZE_MASK) != FUTEX_32)
+ return -EINVAL;
+
+ if (aux.flags & FUTEX_SHARED_FLAG)
+ futex_flags |= FLAGS_SHARED;
+
+ *uaddr = aux.uaddr;
+ *flags = futex_flags;
+
+ return 0;
+}
+
+/**
+ * sys_futex_requeue - Wake futexes at rq1 and requeue from rq1 to rq2
+ * @rq1: Address of futexes to be waken/dequeued
+ * @rq2: Address for the futexes to be enqueued
+ * @nr_wake: Number of futexes waiting in uaddr1 to be woken up
+ * @nr_requeue: Number of futexes to be requeued from uaddr1 to uaddr2
+ * @cmpval: Expected value at uaddr1
+ * @flags: Reserved flags arg for requeue operation expansion. Must be 0.
+ *
+ * If (rq1->uaddr == cmpval), wake at uaddr1->uaddr a nr_wake number of
+ * waiters and then, remove a number of nr_requeue waiters at rq1->uaddr
+ * and add then to rq2->uaddr list. Each uaddr has its own set of flags,
+ * that must be defined at struct futex_requeue (such as size, shared, NUMA).
+ *
+ * Return the number of the woken futexes + the number of requeued ones on
+ * success, error code otherwise.
+ */
+SYSCALL_DEFINE6(futex_requeue, struct futex_requeue __user *, rq1,
+ struct futex_requeue __user *, rq2,
+ unsigned int, nr_wake, unsigned int, nr_requeue,
+ u64, cmpval, unsigned int, flags)
+{
+ void __user *uaddr1, *uaddr2;
+ unsigned int flags1, flags2;
+ u32 val = cmpval;
+ int ret;
+
+ if (flags)
+ return -EINVAL;
+
+ ret = futex_parse_requeue(rq1, &uaddr1, &flags1);
+ if (ret)
+ return ret;
+
+ ret = futex_parse_requeue(rq2, &uaddr2, &flags2);
+ if (ret)
+ return ret;
+
+ return futex_requeue(uaddr1, flags1, uaddr2, flags2, nr_wake, nr_requeue, &val, 0);
+}
@@ -157,6 +157,8 @@ COND_SYSCALL_COMPAT(futex_wait);
COND_SYSCALL(futex_wake);
COND_SYSCALL(futex_waitv);
COND_SYSCALL_COMPAT(futex_waitv);
+COND_SYSCALL(futex_requeue);
+COND_SYSCALL_COMPAT(futex_requeue);
/* kernel/hrtimer.c */