Skip to content

[5.4] Track Steam patches #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/uapi/linux/futex.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 */
214 changes: 190 additions & 24 deletions kernel/futex.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -2629,6 +2630,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
Expand All @@ -2649,7 +2683,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;

/*
Expand All @@ -2670,38 +2703,163 @@ 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);
/* Drop key reference if retry or error. */
if (ret)
put_futex_key(&q->key);
} while (ret > 0);

if (ret) {
queue_unlock(*hb);
return ret;
}

ret = get_user(uval, uaddr);
if (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;
}
}

if (!(flags & FLAGS_SHARED))
goto retry_private;
set_current_state(TASK_INTERRUPTIBLE);

put_futex_key(&q->key);
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;

/* Woken by triggered timeout */
if (to && !to->task)
goto out;

/*
* 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;
}

Expand Down Expand Up @@ -2782,6 +2940,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);
}
Expand Down Expand Up @@ -3665,6 +3827,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;
}
Expand All @@ -3681,7 +3845,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))
Expand All @@ -3690,7 +3855,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;
}
Expand Down Expand Up @@ -3874,14 +4039,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;
}
Expand Down