diff options
Diffstat (limited to 'kernel/locking')
| -rw-r--r-- | kernel/locking/mcs_spinlock.c | 64 | ||||
| -rw-r--r-- | kernel/locking/mcs_spinlock.h | 9 | ||||
| -rw-r--r-- | kernel/locking/mutex.c | 2 | ||||
| -rw-r--r-- | kernel/locking/rtmutex-debug.h | 5 | ||||
| -rw-r--r-- | kernel/locking/rtmutex.c | 243 | ||||
| -rw-r--r-- | kernel/locking/rtmutex.h | 5 | ||||
| -rw-r--r-- | kernel/locking/rwsem-spinlock.c | 28 | ||||
| -rw-r--r-- | kernel/locking/rwsem-xadd.c | 16 | ||||
| -rw-r--r-- | kernel/locking/rwsem.c | 2 | 
9 files changed, 295 insertions, 79 deletions
| diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c index 838dc9e00669..be9ee1559fca 100644 --- a/kernel/locking/mcs_spinlock.c +++ b/kernel/locking/mcs_spinlock.c @@ -14,21 +14,47 @@   * called from interrupt context and we have preemption disabled while   * spinning.   */ -static DEFINE_PER_CPU_SHARED_ALIGNED(struct optimistic_spin_queue, osq_node); +static DEFINE_PER_CPU_SHARED_ALIGNED(struct optimistic_spin_node, osq_node); + +/* + * We use the value 0 to represent "no CPU", thus the encoded value + * will be the CPU number incremented by 1. + */ +static inline int encode_cpu(int cpu_nr) +{ +	return cpu_nr + 1; +} + +static inline struct optimistic_spin_node *decode_cpu(int encoded_cpu_val) +{ +	int cpu_nr = encoded_cpu_val - 1; + +	return per_cpu_ptr(&osq_node, cpu_nr); +}  /*   * Get a stable @node->next pointer, either for unlock() or unqueue() purposes.   * Can return NULL in case we were the last queued and we updated @lock instead.   */ -static inline struct optimistic_spin_queue * -osq_wait_next(struct optimistic_spin_queue **lock, -	      struct optimistic_spin_queue *node, -	      struct optimistic_spin_queue *prev) +static inline struct optimistic_spin_node * +osq_wait_next(struct optimistic_spin_queue *lock, +	      struct optimistic_spin_node *node, +	      struct optimistic_spin_node *prev)  { -	struct optimistic_spin_queue *next = NULL; +	struct optimistic_spin_node *next = NULL; +	int curr = encode_cpu(smp_processor_id()); +	int old; + +	/* +	 * If there is a prev node in queue, then the 'old' value will be +	 * the prev node's CPU #, else it's set to OSQ_UNLOCKED_VAL since if +	 * we're currently last in queue, then the queue will then become empty. +	 */ +	old = prev ? prev->cpu : OSQ_UNLOCKED_VAL;  	for (;;) { -		if (*lock == node && cmpxchg(lock, node, prev) == node) { +		if (atomic_read(&lock->tail) == curr && +		    atomic_cmpxchg(&lock->tail, curr, old) == curr) {  			/*  			 * We were the last queued, we moved @lock back. @prev  			 * will now observe @lock and will complete its @@ -59,18 +85,23 @@ osq_wait_next(struct optimistic_spin_queue **lock,  	return next;  } -bool osq_lock(struct optimistic_spin_queue **lock) +bool osq_lock(struct optimistic_spin_queue *lock)  { -	struct optimistic_spin_queue *node = this_cpu_ptr(&osq_node); -	struct optimistic_spin_queue *prev, *next; +	struct optimistic_spin_node *node = this_cpu_ptr(&osq_node); +	struct optimistic_spin_node *prev, *next; +	int curr = encode_cpu(smp_processor_id()); +	int old;  	node->locked = 0;  	node->next = NULL; +	node->cpu = curr; -	node->prev = prev = xchg(lock, node); -	if (likely(prev == NULL)) +	old = atomic_xchg(&lock->tail, curr); +	if (old == OSQ_UNLOCKED_VAL)  		return true; +	prev = decode_cpu(old); +	node->prev = prev;  	ACCESS_ONCE(prev->next) = node;  	/* @@ -149,20 +180,21 @@ unqueue:  	return false;  } -void osq_unlock(struct optimistic_spin_queue **lock) +void osq_unlock(struct optimistic_spin_queue *lock)  { -	struct optimistic_spin_queue *node = this_cpu_ptr(&osq_node); -	struct optimistic_spin_queue *next; +	struct optimistic_spin_node *node, *next; +	int curr = encode_cpu(smp_processor_id());  	/*  	 * Fast path for the uncontended case.  	 */ -	if (likely(cmpxchg(lock, node, NULL) == node)) +	if (likely(atomic_cmpxchg(&lock->tail, curr, OSQ_UNLOCKED_VAL) == curr))  		return;  	/*  	 * Second most likely case.  	 */ +	node = this_cpu_ptr(&osq_node);  	next = xchg(&node->next, NULL);  	if (next) {  		ACCESS_ONCE(next->locked) = 1; diff --git a/kernel/locking/mcs_spinlock.h b/kernel/locking/mcs_spinlock.h index a2dbac4aca6b..74356dc0ce29 100644 --- a/kernel/locking/mcs_spinlock.h +++ b/kernel/locking/mcs_spinlock.h @@ -118,12 +118,13 @@ void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)   * mutex_lock()/rwsem_down_{read,write}() etc.   */ -struct optimistic_spin_queue { -	struct optimistic_spin_queue *next, *prev; +struct optimistic_spin_node { +	struct optimistic_spin_node *next, *prev;  	int locked; /* 1 if lock acquired */ +	int cpu; /* encoded CPU # value */  }; -extern bool osq_lock(struct optimistic_spin_queue **lock); -extern void osq_unlock(struct optimistic_spin_queue **lock); +extern bool osq_lock(struct optimistic_spin_queue *lock); +extern void osq_unlock(struct optimistic_spin_queue *lock);  #endif /* __LINUX_MCS_SPINLOCK_H */ diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c index bc73d33c6760..acca2c1a3c5e 100644 --- a/kernel/locking/mutex.c +++ b/kernel/locking/mutex.c @@ -60,7 +60,7 @@ __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)  	INIT_LIST_HEAD(&lock->wait_list);  	mutex_clear_owner(lock);  #ifdef CONFIG_MUTEX_SPIN_ON_OWNER -	lock->osq = NULL; +	osq_lock_init(&lock->osq);  #endif  	debug_mutex_init(lock, name, key); diff --git a/kernel/locking/rtmutex-debug.h b/kernel/locking/rtmutex-debug.h index 14193d596d78..ab29b6a22669 100644 --- a/kernel/locking/rtmutex-debug.h +++ b/kernel/locking/rtmutex-debug.h @@ -31,3 +31,8 @@ static inline int debug_rt_mutex_detect_deadlock(struct rt_mutex_waiter *waiter,  {  	return (waiter != NULL);  } + +static inline void rt_mutex_print_deadlock(struct rt_mutex_waiter *w) +{ +	debug_rt_mutex_print_deadlock(w); +} diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index a620d4d08ca6..fc605941b9b8 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -83,6 +83,47 @@ static inline void mark_rt_mutex_waiters(struct rt_mutex *lock)  		owner = *p;  	} while (cmpxchg(p, owner, owner | RT_MUTEX_HAS_WAITERS) != owner);  } + +/* + * Safe fastpath aware unlock: + * 1) Clear the waiters bit + * 2) Drop lock->wait_lock + * 3) Try to unlock the lock with cmpxchg + */ +static inline bool unlock_rt_mutex_safe(struct rt_mutex *lock) +	__releases(lock->wait_lock) +{ +	struct task_struct *owner = rt_mutex_owner(lock); + +	clear_rt_mutex_waiters(lock); +	raw_spin_unlock(&lock->wait_lock); +	/* +	 * If a new waiter comes in between the unlock and the cmpxchg +	 * we have two situations: +	 * +	 * unlock(wait_lock); +	 *					lock(wait_lock); +	 * cmpxchg(p, owner, 0) == owner +	 *					mark_rt_mutex_waiters(lock); +	 *					acquire(lock); +	 * or: +	 * +	 * unlock(wait_lock); +	 *					lock(wait_lock); +	 *					mark_rt_mutex_waiters(lock); +	 * +	 * cmpxchg(p, owner, 0) != owner +	 *					enqueue_waiter(); +	 *					unlock(wait_lock); +	 * lock(wait_lock); +	 * wake waiter(); +	 * unlock(wait_lock); +	 *					lock(wait_lock); +	 *					acquire(lock); +	 */ +	return rt_mutex_cmpxchg(lock, owner, NULL); +} +  #else  # define rt_mutex_cmpxchg(l,c,n)	(0)  static inline void mark_rt_mutex_waiters(struct rt_mutex *lock) @@ -90,6 +131,17 @@ static inline void mark_rt_mutex_waiters(struct rt_mutex *lock)  	lock->owner = (struct task_struct *)  			((unsigned long)lock->owner | RT_MUTEX_HAS_WAITERS);  } + +/* + * Simple slow path only version: lock->owner is protected by lock->wait_lock. + */ +static inline bool unlock_rt_mutex_safe(struct rt_mutex *lock) +	__releases(lock->wait_lock) +{ +	lock->owner = NULL; +	raw_spin_unlock(&lock->wait_lock); +	return true; +}  #endif  static inline int @@ -260,27 +312,36 @@ static void rt_mutex_adjust_prio(struct task_struct *task)   */  int max_lock_depth = 1024; +static inline struct rt_mutex *task_blocked_on_lock(struct task_struct *p) +{ +	return p->pi_blocked_on ? p->pi_blocked_on->lock : NULL; +} +  /*   * Adjust the priority chain. Also used for deadlock detection.   * Decreases task's usage by one - may thus free the task.   * - * @task: the task owning the mutex (owner) for which a chain walk is probably - *	  needed + * @task:	the task owning the mutex (owner) for which a chain walk is + *		probably needed   * @deadlock_detect: do we have to carry out deadlock detection? - * @orig_lock: the mutex (can be NULL if we are walking the chain to recheck - * 	       things for a task that has just got its priority adjusted, and - *	       is waiting on a mutex) + * @orig_lock:	the mutex (can be NULL if we are walking the chain to recheck + *		things for a task that has just got its priority adjusted, and + *		is waiting on a mutex) + * @next_lock:	the mutex on which the owner of @orig_lock was blocked before + *		we dropped its pi_lock. Is never dereferenced, only used for + *		comparison to detect lock chain changes.   * @orig_waiter: rt_mutex_waiter struct for the task that has just donated - *		 its priority to the mutex owner (can be NULL in the case - *		 depicted above or if the top waiter is gone away and we are - *		 actually deboosting the owner) - * @top_task: the current top waiter + *		its priority to the mutex owner (can be NULL in the case + *		depicted above or if the top waiter is gone away and we are + *		actually deboosting the owner) + * @top_task:	the current top waiter   *   * Returns 0 or -EDEADLK.   */  static int rt_mutex_adjust_prio_chain(struct task_struct *task,  				      int deadlock_detect,  				      struct rt_mutex *orig_lock, +				      struct rt_mutex *next_lock,  				      struct rt_mutex_waiter *orig_waiter,  				      struct task_struct *top_task)  { @@ -314,7 +375,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,  		}  		put_task_struct(task); -		return deadlock_detect ? -EDEADLK : 0; +		return -EDEADLK;  	}   retry:  	/* @@ -339,6 +400,18 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,  		goto out_unlock_pi;  	/* +	 * We dropped all locks after taking a refcount on @task, so +	 * the task might have moved on in the lock chain or even left +	 * the chain completely and blocks now on an unrelated lock or +	 * on @orig_lock. +	 * +	 * We stored the lock on which @task was blocked in @next_lock, +	 * so we can detect the chain change. +	 */ +	if (next_lock != waiter->lock) +		goto out_unlock_pi; + +	/*  	 * Drop out, when the task has no waiters. Note,  	 * top_waiter can be NULL, when we are in the deboosting  	 * mode! @@ -377,7 +450,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,  	if (lock == orig_lock || rt_mutex_owner(lock) == top_task) {  		debug_rt_mutex_deadlock(deadlock_detect, orig_waiter, lock);  		raw_spin_unlock(&lock->wait_lock); -		ret = deadlock_detect ? -EDEADLK : 0; +		ret = -EDEADLK;  		goto out_unlock_pi;  	} @@ -422,11 +495,26 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,  		__rt_mutex_adjust_prio(task);  	} +	/* +	 * Check whether the task which owns the current lock is pi +	 * blocked itself. If yes we store a pointer to the lock for +	 * the lock chain change detection above. After we dropped +	 * task->pi_lock next_lock cannot be dereferenced anymore. +	 */ +	next_lock = task_blocked_on_lock(task); +  	raw_spin_unlock_irqrestore(&task->pi_lock, flags);  	top_waiter = rt_mutex_top_waiter(lock);  	raw_spin_unlock(&lock->wait_lock); +	/* +	 * We reached the end of the lock chain. Stop right here. No +	 * point to go back just to figure that out. +	 */ +	if (!next_lock) +		goto out_put_task; +  	if (!detect_deadlock && waiter != top_waiter)  		goto out_put_task; @@ -536,8 +624,9 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,  {  	struct task_struct *owner = rt_mutex_owner(lock);  	struct rt_mutex_waiter *top_waiter = waiter; -	unsigned long flags; +	struct rt_mutex *next_lock;  	int chain_walk = 0, res; +	unsigned long flags;  	/*  	 * Early deadlock detection. We really don't want the task to @@ -548,7 +637,7 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,  	 * which is wrong, as the other waiter is not in a deadlock  	 * situation.  	 */ -	if (detect_deadlock && owner == task) +	if (owner == task)  		return -EDEADLK;  	raw_spin_lock_irqsave(&task->pi_lock, flags); @@ -569,20 +658,28 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,  	if (!owner)  		return 0; +	raw_spin_lock_irqsave(&owner->pi_lock, flags);  	if (waiter == rt_mutex_top_waiter(lock)) { -		raw_spin_lock_irqsave(&owner->pi_lock, flags);  		rt_mutex_dequeue_pi(owner, top_waiter);  		rt_mutex_enqueue_pi(owner, waiter);  		__rt_mutex_adjust_prio(owner);  		if (owner->pi_blocked_on)  			chain_walk = 1; -		raw_spin_unlock_irqrestore(&owner->pi_lock, flags); -	} -	else if (debug_rt_mutex_detect_deadlock(waiter, detect_deadlock)) +	} else if (debug_rt_mutex_detect_deadlock(waiter, detect_deadlock)) {  		chain_walk = 1; +	} -	if (!chain_walk) +	/* Store the lock on which owner is blocked or NULL */ +	next_lock = task_blocked_on_lock(owner); + +	raw_spin_unlock_irqrestore(&owner->pi_lock, flags); +	/* +	 * Even if full deadlock detection is on, if the owner is not +	 * blocked itself, we can avoid finding this out in the chain +	 * walk. +	 */ +	if (!chain_walk || !next_lock)  		return 0;  	/* @@ -594,8 +691,8 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,  	raw_spin_unlock(&lock->wait_lock); -	res = rt_mutex_adjust_prio_chain(owner, detect_deadlock, lock, waiter, -					 task); +	res = rt_mutex_adjust_prio_chain(owner, detect_deadlock, lock, +					 next_lock, waiter, task);  	raw_spin_lock(&lock->wait_lock); @@ -605,7 +702,8 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,  /*   * Wake up the next waiter on the lock.   * - * Remove the top waiter from the current tasks waiter list and wake it up. + * Remove the top waiter from the current tasks pi waiter list and + * wake it up.   *   * Called with lock->wait_lock held.   */ @@ -626,10 +724,23 @@ static void wakeup_next_waiter(struct rt_mutex *lock)  	 */  	rt_mutex_dequeue_pi(current, waiter); -	rt_mutex_set_owner(lock, NULL); +	/* +	 * As we are waking up the top waiter, and the waiter stays +	 * queued on the lock until it gets the lock, this lock +	 * obviously has waiters. Just set the bit here and this has +	 * the added benefit of forcing all new tasks into the +	 * slow path making sure no task of lower priority than +	 * the top waiter can steal this lock. +	 */ +	lock->owner = (void *) RT_MUTEX_HAS_WAITERS;  	raw_spin_unlock_irqrestore(¤t->pi_lock, flags); +	/* +	 * It's safe to dereference waiter as it cannot go away as +	 * long as we hold lock->wait_lock. The waiter task needs to +	 * acquire it in order to dequeue the waiter. +	 */  	wake_up_process(waiter->task);  } @@ -644,8 +755,8 @@ static void remove_waiter(struct rt_mutex *lock,  {  	int first = (waiter == rt_mutex_top_waiter(lock));  	struct task_struct *owner = rt_mutex_owner(lock); +	struct rt_mutex *next_lock = NULL;  	unsigned long flags; -	int chain_walk = 0;  	raw_spin_lock_irqsave(¤t->pi_lock, flags);  	rt_mutex_dequeue(lock, waiter); @@ -669,13 +780,13 @@ static void remove_waiter(struct rt_mutex *lock,  		}  		__rt_mutex_adjust_prio(owner); -		if (owner->pi_blocked_on) -			chain_walk = 1; +		/* Store the lock on which owner is blocked or NULL */ +		next_lock = task_blocked_on_lock(owner);  		raw_spin_unlock_irqrestore(&owner->pi_lock, flags);  	} -	if (!chain_walk) +	if (!next_lock)  		return;  	/* gets dropped in rt_mutex_adjust_prio_chain()! */ @@ -683,7 +794,7 @@ static void remove_waiter(struct rt_mutex *lock,  	raw_spin_unlock(&lock->wait_lock); -	rt_mutex_adjust_prio_chain(owner, 0, lock, NULL, current); +	rt_mutex_adjust_prio_chain(owner, 0, lock, next_lock, NULL, current);  	raw_spin_lock(&lock->wait_lock);  } @@ -696,6 +807,7 @@ static void remove_waiter(struct rt_mutex *lock,  void rt_mutex_adjust_pi(struct task_struct *task)  {  	struct rt_mutex_waiter *waiter; +	struct rt_mutex *next_lock;  	unsigned long flags;  	raw_spin_lock_irqsave(&task->pi_lock, flags); @@ -706,12 +818,13 @@ void rt_mutex_adjust_pi(struct task_struct *task)  		raw_spin_unlock_irqrestore(&task->pi_lock, flags);  		return;  	} - +	next_lock = waiter->lock;  	raw_spin_unlock_irqrestore(&task->pi_lock, flags);  	/* gets dropped in rt_mutex_adjust_prio_chain()! */  	get_task_struct(task); -	rt_mutex_adjust_prio_chain(task, 0, NULL, NULL, task); + +	rt_mutex_adjust_prio_chain(task, 0, NULL, next_lock, NULL, task);  }  /** @@ -763,6 +876,26 @@ __rt_mutex_slowlock(struct rt_mutex *lock, int state,  	return ret;  } +static void rt_mutex_handle_deadlock(int res, int detect_deadlock, +				     struct rt_mutex_waiter *w) +{ +	/* +	 * If the result is not -EDEADLOCK or the caller requested +	 * deadlock detection, nothing to do here. +	 */ +	if (res != -EDEADLOCK || detect_deadlock) +		return; + +	/* +	 * Yell lowdly and stop the task right here. +	 */ +	rt_mutex_print_deadlock(w); +	while (1) { +		set_current_state(TASK_INTERRUPTIBLE); +		schedule(); +	} +} +  /*   * Slow path lock function:   */ @@ -802,8 +935,10 @@ rt_mutex_slowlock(struct rt_mutex *lock, int state,  	set_current_state(TASK_RUNNING); -	if (unlikely(ret)) +	if (unlikely(ret)) {  		remove_waiter(lock, &waiter); +		rt_mutex_handle_deadlock(ret, detect_deadlock, &waiter); +	}  	/*  	 * try_to_take_rt_mutex() sets the waiter bit @@ -859,12 +994,49 @@ rt_mutex_slowunlock(struct rt_mutex *lock)  	rt_mutex_deadlock_account_unlock(current); -	if (!rt_mutex_has_waiters(lock)) { -		lock->owner = NULL; -		raw_spin_unlock(&lock->wait_lock); -		return; +	/* +	 * We must be careful here if the fast path is enabled. If we +	 * have no waiters queued we cannot set owner to NULL here +	 * because of: +	 * +	 * foo->lock->owner = NULL; +	 *			rtmutex_lock(foo->lock);   <- fast path +	 *			free = atomic_dec_and_test(foo->refcnt); +	 *			rtmutex_unlock(foo->lock); <- fast path +	 *			if (free) +	 *				kfree(foo); +	 * raw_spin_unlock(foo->lock->wait_lock); +	 * +	 * So for the fastpath enabled kernel: +	 * +	 * Nothing can set the waiters bit as long as we hold +	 * lock->wait_lock. So we do the following sequence: +	 * +	 *	owner = rt_mutex_owner(lock); +	 *	clear_rt_mutex_waiters(lock); +	 *	raw_spin_unlock(&lock->wait_lock); +	 *	if (cmpxchg(&lock->owner, owner, 0) == owner) +	 *		return; +	 *	goto retry; +	 * +	 * The fastpath disabled variant is simple as all access to +	 * lock->owner is serialized by lock->wait_lock: +	 * +	 *	lock->owner = NULL; +	 *	raw_spin_unlock(&lock->wait_lock); +	 */ +	while (!rt_mutex_has_waiters(lock)) { +		/* Drops lock->wait_lock ! */ +		if (unlock_rt_mutex_safe(lock) == true) +			return; +		/* Relock the rtmutex and try again */ +		raw_spin_lock(&lock->wait_lock);  	} +	/* +	 * The wakeup next waiter path does not suffer from the above +	 * race. See the comments there. +	 */  	wakeup_next_waiter(lock);  	raw_spin_unlock(&lock->wait_lock); @@ -1112,7 +1284,8 @@ int rt_mutex_start_proxy_lock(struct rt_mutex *lock,  		return 1;  	} -	ret = task_blocks_on_rt_mutex(lock, waiter, task, detect_deadlock); +	/* We enforce deadlock detection for futexes */ +	ret = task_blocks_on_rt_mutex(lock, waiter, task, 1);  	if (ret && !rt_mutex_owner(lock)) {  		/* diff --git a/kernel/locking/rtmutex.h b/kernel/locking/rtmutex.h index a1a1dd06421d..f6a1f3c133b1 100644 --- a/kernel/locking/rtmutex.h +++ b/kernel/locking/rtmutex.h @@ -24,3 +24,8 @@  #define debug_rt_mutex_print_deadlock(w)		do { } while (0)  #define debug_rt_mutex_detect_deadlock(w,d)		(d)  #define debug_rt_mutex_reset_waiter(w)			do { } while (0) + +static inline void rt_mutex_print_deadlock(struct rt_mutex_waiter *w) +{ +	WARN(1, "rtmutex deadlock detected\n"); +} diff --git a/kernel/locking/rwsem-spinlock.c b/kernel/locking/rwsem-spinlock.c index 9be8a9144978..2c93571162cb 100644 --- a/kernel/locking/rwsem-spinlock.c +++ b/kernel/locking/rwsem-spinlock.c @@ -26,7 +26,7 @@ int rwsem_is_locked(struct rw_semaphore *sem)  	unsigned long flags;  	if (raw_spin_trylock_irqsave(&sem->wait_lock, flags)) { -		ret = (sem->activity != 0); +		ret = (sem->count != 0);  		raw_spin_unlock_irqrestore(&sem->wait_lock, flags);  	}  	return ret; @@ -46,7 +46,7 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name,  	debug_check_no_locks_freed((void *)sem, sizeof(*sem));  	lockdep_init_map(&sem->dep_map, name, key, 0);  #endif -	sem->activity = 0; +	sem->count = 0;  	raw_spin_lock_init(&sem->wait_lock);  	INIT_LIST_HEAD(&sem->wait_list);  } @@ -95,7 +95,7 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wakewrite)  		waiter = list_entry(next, struct rwsem_waiter, list);  	} while (waiter->type != RWSEM_WAITING_FOR_WRITE); -	sem->activity += woken; +	sem->count += woken;   out:  	return sem; @@ -126,9 +126,9 @@ void __sched __down_read(struct rw_semaphore *sem)  	raw_spin_lock_irqsave(&sem->wait_lock, flags); -	if (sem->activity >= 0 && list_empty(&sem->wait_list)) { +	if (sem->count >= 0 && list_empty(&sem->wait_list)) {  		/* granted */ -		sem->activity++; +		sem->count++;  		raw_spin_unlock_irqrestore(&sem->wait_lock, flags);  		goto out;  	} @@ -170,9 +170,9 @@ int __down_read_trylock(struct rw_semaphore *sem)  	raw_spin_lock_irqsave(&sem->wait_lock, flags); -	if (sem->activity >= 0 && list_empty(&sem->wait_list)) { +	if (sem->count >= 0 && list_empty(&sem->wait_list)) {  		/* granted */ -		sem->activity++; +		sem->count++;  		ret = 1;  	} @@ -206,7 +206,7 @@ void __sched __down_write_nested(struct rw_semaphore *sem, int subclass)  		 * itself into sleep and waiting for system woke it or someone  		 * else in the head of the wait list up.  		 */ -		if (sem->activity == 0) +		if (sem->count == 0)  			break;  		set_task_state(tsk, TASK_UNINTERRUPTIBLE);  		raw_spin_unlock_irqrestore(&sem->wait_lock, flags); @@ -214,7 +214,7 @@ void __sched __down_write_nested(struct rw_semaphore *sem, int subclass)  		raw_spin_lock_irqsave(&sem->wait_lock, flags);  	}  	/* got the lock */ -	sem->activity = -1; +	sem->count = -1;  	list_del(&waiter.list);  	raw_spin_unlock_irqrestore(&sem->wait_lock, flags); @@ -235,9 +235,9 @@ int __down_write_trylock(struct rw_semaphore *sem)  	raw_spin_lock_irqsave(&sem->wait_lock, flags); -	if (sem->activity == 0) { +	if (sem->count == 0) {  		/* got the lock */ -		sem->activity = -1; +		sem->count = -1;  		ret = 1;  	} @@ -255,7 +255,7 @@ void __up_read(struct rw_semaphore *sem)  	raw_spin_lock_irqsave(&sem->wait_lock, flags); -	if (--sem->activity == 0 && !list_empty(&sem->wait_list)) +	if (--sem->count == 0 && !list_empty(&sem->wait_list))  		sem = __rwsem_wake_one_writer(sem);  	raw_spin_unlock_irqrestore(&sem->wait_lock, flags); @@ -270,7 +270,7 @@ void __up_write(struct rw_semaphore *sem)  	raw_spin_lock_irqsave(&sem->wait_lock, flags); -	sem->activity = 0; +	sem->count = 0;  	if (!list_empty(&sem->wait_list))  		sem = __rwsem_do_wake(sem, 1); @@ -287,7 +287,7 @@ void __downgrade_write(struct rw_semaphore *sem)  	raw_spin_lock_irqsave(&sem->wait_lock, flags); -	sem->activity = 1; +	sem->count = 1;  	if (!list_empty(&sem->wait_list))  		sem = __rwsem_do_wake(sem, 0); diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c index dacc32142fcc..a2391ac135c8 100644 --- a/kernel/locking/rwsem-xadd.c +++ b/kernel/locking/rwsem-xadd.c @@ -82,9 +82,9 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name,  	sem->count = RWSEM_UNLOCKED_VALUE;  	raw_spin_lock_init(&sem->wait_lock);  	INIT_LIST_HEAD(&sem->wait_list); -#ifdef CONFIG_SMP +#ifdef CONFIG_RWSEM_SPIN_ON_OWNER  	sem->owner = NULL; -	sem->osq = NULL; +	osq_lock_init(&sem->osq);  #endif  } @@ -262,7 +262,7 @@ static inline bool rwsem_try_write_lock(long count, struct rw_semaphore *sem)  	return false;  } -#ifdef CONFIG_SMP +#ifdef CONFIG_RWSEM_SPIN_ON_OWNER  /*   * Try to acquire write lock before the writer has been put on wait queue.   */ @@ -285,10 +285,10 @@ static inline bool rwsem_try_write_lock_unqueued(struct rw_semaphore *sem)  static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)  {  	struct task_struct *owner; -	bool on_cpu = true; +	bool on_cpu = false;  	if (need_resched()) -		return 0; +		return false;  	rcu_read_lock();  	owner = ACCESS_ONCE(sem->owner); @@ -297,9 +297,9 @@ static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)  	rcu_read_unlock();  	/* -	 * If sem->owner is not set, the rwsem owner may have -	 * just acquired it and not set the owner yet or the rwsem -	 * has been released. +	 * If sem->owner is not set, yet we have just recently entered the +	 * slowpath, then there is a possibility reader(s) may have the lock. +	 * To be safe, avoid spinning in these situations.  	 */  	return on_cpu;  } diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index 42f806de49d4..e2d3bc7f03b4 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c @@ -12,7 +12,7 @@  #include <linux/atomic.h> -#if defined(CONFIG_SMP) && defined(CONFIG_RWSEM_XCHGADD_ALGORITHM) +#ifdef CONFIG_RWSEM_SPIN_ON_OWNER  static inline void rwsem_set_owner(struct rw_semaphore *sem)  {  	sem->owner = current; |