diff options
| author | Darren Hart <dvhltc@us.ibm.com> | 2009-04-03 13:40:40 -0700 | 
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2009-04-06 11:14:02 +0200 | 
| commit | f801073f87aa22ddf0e9146355fec3993163790f (patch) | |
| tree | 5499760cfb1abcebdda8868ec0074ae3ff4da0d5 /kernel | |
| parent | 9121e4783cd5c7e2a407763f3b61c2d573891133 (diff) | |
futex: split out futex value validation code
Refactor the code to validate the expected futex value in order to
reuse it with the requeue_pi code.
Signed-off-by: Darren Hart <dvhltc@us.ibm.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/futex.c | 116 | 
1 files changed, 72 insertions, 44 deletions
| diff --git a/kernel/futex.c b/kernel/futex.c index e76942e2a79f..dbe857aa4381 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -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: @@ -1450,33 +1437,74 @@ retry_private:  	 * 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); | 
