Skip to content

Commit

Permalink
futex: split out futex value validation code
Browse files Browse the repository at this point in the history
Refactor the code to validate the expected futex value in order to
reuse it with the requeue_pi code.

Signed-off-by: Darren Hart <[email protected]>
Reviewed-by: Thomas Gleixner <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
  • Loading branch information
dvhart authored and KAGA-KOKO committed Apr 6, 2009
1 parent 9121e47 commit f801073
Showing 1 changed file with 72 additions and 44 deletions.
116 changes: 72 additions & 44 deletions kernel/futex.c
Original file line number Diff line number Diff line change
Expand Up @@ -1398,42 +1398,29 @@ static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,
__set_current_state(TASK_RUNNING);
}

static int futex_wait(u32 __user *uaddr, int fshared,
u32 val, ktime_t *abs_time, u32 bitset, int clockrt)
/**
* futex_wait_setup() - Prepare to wait on a futex
* @uaddr: the futex userspace address
* @val: the expected value
* @fshared: whether the futex is shared (1) or not (0)
* @q: the associated futex_q
* @hb: storage for hash_bucket pointer to be returned to caller
*
* Setup the futex_q and locate the hash_bucket. Get the futex value and
* compare it with the expected value. Handle atomic faults internally.
* Return with the hb lock held and a q.key reference on success, and unlocked
* with no q.key reference on failure.
*
* Returns:
* 0 - uaddr contains val and hb has been locked
* <1 - -EFAULT or -EWOULDBLOCK (uaddr does not contain val) and hb is unlcoked
*/
static int futex_wait_setup(u32 __user *uaddr, u32 val, int fshared,
struct futex_q *q, struct futex_hash_bucket **hb)
{
struct hrtimer_sleeper timeout, *to = NULL;
DECLARE_WAITQUEUE(wait, current);
struct restart_block *restart;
struct futex_hash_bucket *hb;
struct futex_q q;
u32 uval;
int ret;

if (!bitset)
return -EINVAL;

q.pi_state = NULL;
q.bitset = bitset;

if (abs_time) {
to = &timeout;

hrtimer_init_on_stack(&to->timer, clockrt ? CLOCK_REALTIME :
CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
hrtimer_init_sleeper(to, current);
hrtimer_set_expires_range_ns(&to->timer, *abs_time,
current->timer_slack_ns);
}

retry:
q.key = FUTEX_KEY_INIT;
ret = get_futex_key(uaddr, fshared, &q.key);
if (unlikely(ret != 0))
goto out;

retry_private:
hb = queue_lock(&q);

/*
* Access the page AFTER the hash-bucket is locked.
* Order is important:
Expand All @@ -1450,33 +1437,74 @@ static int futex_wait(u32 __user *uaddr, int fshared,
* A consequence is that futex_wait() can return zero and absorb
* a wakeup when *uaddr != val on entry to the syscall. This is
* rare, but normal.
*
* For shared futexes, we hold the mmap semaphore, so the mapping
* cannot have changed since we looked it up in get_futex_key.
*/
retry:
q->key = FUTEX_KEY_INIT;
ret = get_futex_key(uaddr, fshared, &q->key);
if (unlikely(ret != 0))
goto out;

retry_private:
*hb = queue_lock(q);

ret = get_futex_value_locked(&uval, uaddr);

if (unlikely(ret)) {
queue_unlock(&q, hb);
if (ret) {
queue_unlock(q, *hb);

ret = get_user(uval, uaddr);
if (ret)
goto out_put_key;
goto out;

if (!fshared)
goto retry_private;

put_futex_key(fshared, &q.key);
put_futex_key(fshared, &q->key);
goto retry;
}
ret = -EWOULDBLOCK;

/* Only actually queue if *uaddr contained val. */
if (unlikely(uval != val)) {
queue_unlock(&q, hb);
goto out_put_key;
if (uval != val) {
queue_unlock(q, *hb);
ret = -EWOULDBLOCK;
}

out:
if (ret)
put_futex_key(fshared, &q->key);
return ret;
}

static int futex_wait(u32 __user *uaddr, int fshared,
u32 val, ktime_t *abs_time, u32 bitset, int clockrt)
{
struct hrtimer_sleeper timeout, *to = NULL;
DECLARE_WAITQUEUE(wait, current);
struct restart_block *restart;
struct futex_hash_bucket *hb;
struct futex_q q;
int ret;

if (!bitset)
return -EINVAL;

q.pi_state = NULL;
q.bitset = bitset;

if (abs_time) {
to = &timeout;

hrtimer_init_on_stack(&to->timer, clockrt ? CLOCK_REALTIME :
CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
hrtimer_init_sleeper(to, current);
hrtimer_set_expires_range_ns(&to->timer, *abs_time,
current->timer_slack_ns);
}

/* Prepare to wait on uaddr. */
ret = futex_wait_setup(uaddr, val, fshared, &q, &hb);
if (ret)
goto out;

/* queue_me and wait for wakeup, timeout, or a signal. */
futex_wait_queue_me(hb, &q, to, &wait);

Expand Down

0 comments on commit f801073

Please sign in to comment.