diff options
Diffstat (limited to 'kernel/futex.c')
| -rw-r--r-- | kernel/futex.c | 59 | 
1 files changed, 39 insertions, 20 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index 3717e7b306e..19eb089ca00 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -716,7 +716,7 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,  				struct futex_pi_state **ps,  				struct task_struct *task, int set_waiters)  { -	int lock_taken, ret, ownerdied = 0; +	int lock_taken, ret, force_take = 0;  	u32 uval, newval, curval, vpid = task_pid_vnr(task);  retry: @@ -755,17 +755,15 @@ retry:  	newval = curval | FUTEX_WAITERS;  	/* -	 * There are two cases, where a futex might have no owner (the -	 * owner TID is 0): OWNER_DIED. We take over the futex in this -	 * case. We also do an unconditional take over, when the owner -	 * of the futex died. -	 * -	 * This is safe as we are protected by the hash bucket lock ! +	 * Should we force take the futex? See below.  	 */ -	if (unlikely(ownerdied || !(curval & FUTEX_TID_MASK))) { -		/* Keep the OWNER_DIED bit */ +	if (unlikely(force_take)) { +		/* +		 * Keep the OWNER_DIED and the WAITERS bit and set the +		 * new TID value. +		 */  		newval = (curval & ~FUTEX_TID_MASK) | vpid; -		ownerdied = 0; +		force_take = 0;  		lock_taken = 1;  	} @@ -775,7 +773,7 @@ retry:  		goto retry;  	/* -	 * We took the lock due to owner died take over. +	 * We took the lock due to forced take over.  	 */  	if (unlikely(lock_taken))  		return 1; @@ -790,20 +788,25 @@ retry:  		switch (ret) {  		case -ESRCH:  			/* -			 * No owner found for this futex. Check if the -			 * OWNER_DIED bit is set to figure out whether -			 * this is a robust futex or not. +			 * We failed to find an owner for this +			 * futex. So we have no pi_state to block +			 * on. This can happen in two cases: +			 * +			 * 1) The owner died +			 * 2) A stale FUTEX_WAITERS bit +			 * +			 * Re-read the futex value.  			 */  			if (get_futex_value_locked(&curval, uaddr))  				return -EFAULT;  			/* -			 * We simply start over in case of a robust -			 * futex. The code above will take the futex -			 * and return happy. +			 * If the owner died or we have a stale +			 * WAITERS bit the owner TID in the user space +			 * futex is 0.  			 */ -			if (curval & FUTEX_OWNER_DIED) { -				ownerdied = 1; +			if (!(curval & FUTEX_TID_MASK)) { +				force_take = 1;  				goto retry;  			}  		default: @@ -840,6 +843,9 @@ static void wake_futex(struct futex_q *q)  {  	struct task_struct *p = q->task; +	if (WARN(q->pi_state || q->rt_waiter, "refusing to wake PI futex\n")) +		return; +  	/*  	 * We set q->lock_ptr = NULL _before_ we wake up the task. If  	 * a non-futex wake up happens on another CPU then the task @@ -1075,6 +1081,10 @@ retry_private:  	plist_for_each_entry_safe(this, next, head, list) {  		if (match_futex (&this->key, &key1)) { +			if (this->pi_state || this->rt_waiter) { +				ret = -EINVAL; +				goto out_unlock; +			}  			wake_futex(this);  			if (++ret >= nr_wake)  				break; @@ -1087,6 +1097,10 @@ retry_private:  		op_ret = 0;  		plist_for_each_entry_safe(this, next, head, list) {  			if (match_futex (&this->key, &key2)) { +				if (this->pi_state || this->rt_waiter) { +					ret = -EINVAL; +					goto out_unlock; +				}  				wake_futex(this);  				if (++op_ret >= nr_wake2)  					break; @@ -1095,6 +1109,7 @@ retry_private:  		ret += op_ret;  	} +out_unlock:  	double_unlock_hb(hb1, hb2);  out_put_keys:  	put_futex_key(&key2); @@ -1384,9 +1399,13 @@ retry_private:  		/*  		 * FUTEX_WAIT_REQEUE_PI and FUTEX_CMP_REQUEUE_PI should always  		 * be paired with each other and no other futex ops. +		 * +		 * We should never be requeueing a futex_q with a pi_state, +		 * which is awaiting a futex_unlock_pi().  		 */  		if ((requeue_pi && !this->rt_waiter) || -		    (!requeue_pi && this->rt_waiter)) { +		    (!requeue_pi && this->rt_waiter) || +		    this->pi_state) {  			ret = -EINVAL;  			break;  		}  |