Merge branch 'locking-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull locking updates from Ingo Molnar: - Add 'cross-release' support to lockdep, which allows APIs like completions, where it's not the 'owner' who releases the lock, to be tracked. It's all activated automatically under CONFIG_PROVE_LOCKING=y. - Clean up (restructure) the x86 atomics op implementation to be more readable, in preparation of KASAN annotations. (Dmitry Vyukov) - Fix static keys (Paolo Bonzini) - Add killable versions of down_read() et al (Kirill Tkhai) - Rework and fix jump_label locking (Marc Zyngier, Paolo Bonzini) - Rework (and fix) tlb_flush_pending() barriers (Peter Zijlstra) - Remove smp_mb__before_spinlock() and convert its usages, introduce smp_mb__after_spinlock() (Peter Zijlstra) * 'locking-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (56 commits) locking/lockdep/selftests: Fix mixed read-write ABBA tests sched/completion: Avoid unnecessary stack allocation for COMPLETION_INITIALIZER_ONSTACK() acpi/nfit: Fix COMPLETION_INITIALIZER_ONSTACK() abuse locking/pvqspinlock: Relax cmpxchg's to improve performance on some architectures smp: Avoid using two cache lines for struct call_single_data locking/lockdep: Untangle xhlock history save/restore from task independence locking/refcounts, x86/asm: Disable CONFIG_ARCH_HAS_REFCOUNT for the time being futex: Remove duplicated code and fix undefined behaviour Documentation/locking/atomic: Finish the document... locking/lockdep: Fix workqueue crossrelease annotation workqueue/lockdep: 'Fix' flush_work() annotation locking/lockdep/selftests: Add mixed read-write ABBA tests mm, locking/barriers: Clarify tlb_flush_pending() barriers locking/lockdep: Make CONFIG_LOCKDEP_CROSSRELEASE and CONFIG_LOCKDEP_COMPLETIONS truly non-interactive locking/lockdep: Explicitly initialize wq_barrier::done::map locking/lockdep: Rename CONFIG_LOCKDEP_COMPLETE to CONFIG_LOCKDEP_COMPLETIONS locking/lockdep: Reword title of LOCKDEP_CROSSRELEASE config locking/lockdep: Make CONFIG_LOCKDEP_CROSSRELEASE part of CONFIG_PROVE_LOCKING locking/refcounts, x86/asm: Implement fast refcount overflow protection locking/lockdep: Fix the rollback and overwrite detection logic in crossrelease ...
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -143,6 +143,8 @@ struct lockdep_stats {
|
||||
int redundant_softirqs_on;
|
||||
int redundant_softirqs_off;
|
||||
int nr_unused_locks;
|
||||
int nr_redundant_checks;
|
||||
int nr_redundant;
|
||||
int nr_cyclic_checks;
|
||||
int nr_cyclic_check_recursions;
|
||||
int nr_find_usage_forwards_checks;
|
||||
|
@@ -201,6 +201,10 @@ static void lockdep_stats_debug_show(struct seq_file *m)
|
||||
debug_atomic_read(chain_lookup_hits));
|
||||
seq_printf(m, " cyclic checks: %11llu\n",
|
||||
debug_atomic_read(nr_cyclic_checks));
|
||||
seq_printf(m, " redundant checks: %11llu\n",
|
||||
debug_atomic_read(nr_redundant_checks));
|
||||
seq_printf(m, " redundant links: %11llu\n",
|
||||
debug_atomic_read(nr_redundant));
|
||||
seq_printf(m, " find-mask forwards checks: %11llu\n",
|
||||
debug_atomic_read(nr_find_usage_forwards_checks));
|
||||
seq_printf(m, " find-mask backwards checks: %11llu\n",
|
||||
|
@@ -6,4 +6,3 @@
|
||||
*/
|
||||
LOCKDEP_STATE(HARDIRQ)
|
||||
LOCKDEP_STATE(SOFTIRQ)
|
||||
LOCKDEP_STATE(RECLAIM_FS)
|
||||
|
@@ -109,6 +109,19 @@ bool osq_lock(struct optimistic_spin_queue *lock)
|
||||
|
||||
prev = decode_cpu(old);
|
||||
node->prev = prev;
|
||||
|
||||
/*
|
||||
* osq_lock() unqueue
|
||||
*
|
||||
* node->prev = prev osq_wait_next()
|
||||
* WMB MB
|
||||
* prev->next = node next->prev = prev // unqueue-C
|
||||
*
|
||||
* Here 'node->prev' and 'next->prev' are the same variable and we need
|
||||
* to ensure these stores happen in-order to avoid corrupting the list.
|
||||
*/
|
||||
smp_wmb();
|
||||
|
||||
WRITE_ONCE(prev->next, node);
|
||||
|
||||
/*
|
||||
|
@@ -72,7 +72,7 @@ static inline bool pv_queued_spin_steal_lock(struct qspinlock *lock)
|
||||
struct __qspinlock *l = (void *)lock;
|
||||
|
||||
if (!(atomic_read(&lock->val) & _Q_LOCKED_PENDING_MASK) &&
|
||||
(cmpxchg(&l->locked, 0, _Q_LOCKED_VAL) == 0)) {
|
||||
(cmpxchg_acquire(&l->locked, 0, _Q_LOCKED_VAL) == 0)) {
|
||||
qstat_inc(qstat_pv_lock_stealing, true);
|
||||
return true;
|
||||
}
|
||||
@@ -101,16 +101,16 @@ static __always_inline void clear_pending(struct qspinlock *lock)
|
||||
|
||||
/*
|
||||
* The pending bit check in pv_queued_spin_steal_lock() isn't a memory
|
||||
* barrier. Therefore, an atomic cmpxchg() is used to acquire the lock
|
||||
* just to be sure that it will get it.
|
||||
* barrier. Therefore, an atomic cmpxchg_acquire() is used to acquire the
|
||||
* lock just to be sure that it will get it.
|
||||
*/
|
||||
static __always_inline int trylock_clear_pending(struct qspinlock *lock)
|
||||
{
|
||||
struct __qspinlock *l = (void *)lock;
|
||||
|
||||
return !READ_ONCE(l->locked) &&
|
||||
(cmpxchg(&l->locked_pending, _Q_PENDING_VAL, _Q_LOCKED_VAL)
|
||||
== _Q_PENDING_VAL);
|
||||
(cmpxchg_acquire(&l->locked_pending, _Q_PENDING_VAL,
|
||||
_Q_LOCKED_VAL) == _Q_PENDING_VAL);
|
||||
}
|
||||
#else /* _Q_PENDING_BITS == 8 */
|
||||
static __always_inline void set_pending(struct qspinlock *lock)
|
||||
@@ -138,7 +138,7 @@ static __always_inline int trylock_clear_pending(struct qspinlock *lock)
|
||||
*/
|
||||
old = val;
|
||||
new = (val & ~_Q_PENDING_MASK) | _Q_LOCKED_VAL;
|
||||
val = atomic_cmpxchg(&lock->val, old, new);
|
||||
val = atomic_cmpxchg_acquire(&lock->val, old, new);
|
||||
|
||||
if (val == old)
|
||||
return 1;
|
||||
@@ -362,8 +362,18 @@ static void pv_kick_node(struct qspinlock *lock, struct mcs_spinlock *node)
|
||||
* observe its next->locked value and advance itself.
|
||||
*
|
||||
* Matches with smp_store_mb() and cmpxchg() in pv_wait_node()
|
||||
*
|
||||
* The write to next->locked in arch_mcs_spin_unlock_contended()
|
||||
* must be ordered before the read of pn->state in the cmpxchg()
|
||||
* below for the code to work correctly. To guarantee full ordering
|
||||
* irrespective of the success or failure of the cmpxchg(),
|
||||
* a relaxed version with explicit barrier is used. The control
|
||||
* dependency will order the reading of pn->state before any
|
||||
* subsequent writes.
|
||||
*/
|
||||
if (cmpxchg(&pn->state, vcpu_halted, vcpu_hashed) != vcpu_halted)
|
||||
smp_mb__before_atomic();
|
||||
if (cmpxchg_relaxed(&pn->state, vcpu_halted, vcpu_hashed)
|
||||
!= vcpu_halted)
|
||||
return;
|
||||
|
||||
/*
|
||||
|
@@ -40,6 +40,9 @@ struct rt_mutex_waiter {
|
||||
/*
|
||||
* Various helpers to access the waiters-tree:
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_RT_MUTEXES
|
||||
|
||||
static inline int rt_mutex_has_waiters(struct rt_mutex *lock)
|
||||
{
|
||||
return !RB_EMPTY_ROOT(&lock->waiters);
|
||||
@@ -69,6 +72,32 @@ task_top_pi_waiter(struct task_struct *p)
|
||||
pi_tree_entry);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int rt_mutex_has_waiters(struct rt_mutex *lock)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct rt_mutex_waiter *
|
||||
rt_mutex_top_waiter(struct rt_mutex *lock)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int task_has_pi_waiters(struct task_struct *p)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct rt_mutex_waiter *
|
||||
task_top_pi_waiter(struct task_struct *p)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* lock->owner state tracking:
|
||||
*/
|
||||
|
@@ -126,7 +126,7 @@ __rwsem_wake_one_writer(struct rw_semaphore *sem)
|
||||
/*
|
||||
* get a read lock on the semaphore
|
||||
*/
|
||||
void __sched __down_read(struct rw_semaphore *sem)
|
||||
int __sched __down_read_common(struct rw_semaphore *sem, int state)
|
||||
{
|
||||
struct rwsem_waiter waiter;
|
||||
unsigned long flags;
|
||||
@@ -140,8 +140,6 @@ void __sched __down_read(struct rw_semaphore *sem)
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
|
||||
/* set up my own style of waitqueue */
|
||||
waiter.task = current;
|
||||
waiter.type = RWSEM_WAITING_FOR_READ;
|
||||
@@ -149,20 +147,41 @@ void __sched __down_read(struct rw_semaphore *sem)
|
||||
|
||||
list_add_tail(&waiter.list, &sem->wait_list);
|
||||
|
||||
/* we don't need to touch the semaphore struct anymore */
|
||||
raw_spin_unlock_irqrestore(&sem->wait_lock, flags);
|
||||
|
||||
/* wait to be given the lock */
|
||||
for (;;) {
|
||||
if (!waiter.task)
|
||||
break;
|
||||
if (signal_pending_state(state, current))
|
||||
goto out_nolock;
|
||||
set_current_state(state);
|
||||
raw_spin_unlock_irqrestore(&sem->wait_lock, flags);
|
||||
schedule();
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
raw_spin_lock_irqsave(&sem->wait_lock, flags);
|
||||
}
|
||||
|
||||
__set_current_state(TASK_RUNNING);
|
||||
raw_spin_unlock_irqrestore(&sem->wait_lock, flags);
|
||||
out:
|
||||
;
|
||||
return 0;
|
||||
|
||||
out_nolock:
|
||||
/*
|
||||
* We didn't take the lock, so that there is a writer, which
|
||||
* is owner or the first waiter of the sem. If it's a waiter,
|
||||
* it will be woken by current owner. Not need to wake anybody.
|
||||
*/
|
||||
list_del(&waiter.list);
|
||||
raw_spin_unlock_irqrestore(&sem->wait_lock, flags);
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
void __sched __down_read(struct rw_semaphore *sem)
|
||||
{
|
||||
__down_read_common(sem, TASK_UNINTERRUPTIBLE);
|
||||
}
|
||||
|
||||
int __sched __down_read_killable(struct rw_semaphore *sem)
|
||||
{
|
||||
return __down_read_common(sem, TASK_KILLABLE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -221,8 +221,8 @@ static void __rwsem_mark_wake(struct rw_semaphore *sem,
|
||||
/*
|
||||
* Wait for the read lock to be granted
|
||||
*/
|
||||
__visible
|
||||
struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
|
||||
static inline struct rw_semaphore __sched *
|
||||
__rwsem_down_read_failed_common(struct rw_semaphore *sem, int state)
|
||||
{
|
||||
long count, adjustment = -RWSEM_ACTIVE_READ_BIAS;
|
||||
struct rwsem_waiter waiter;
|
||||
@@ -255,17 +255,44 @@ struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
|
||||
|
||||
/* wait to be given the lock */
|
||||
while (true) {
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
set_current_state(state);
|
||||
if (!waiter.task)
|
||||
break;
|
||||
if (signal_pending_state(state, current)) {
|
||||
raw_spin_lock_irq(&sem->wait_lock);
|
||||
if (waiter.task)
|
||||
goto out_nolock;
|
||||
raw_spin_unlock_irq(&sem->wait_lock);
|
||||
break;
|
||||
}
|
||||
schedule();
|
||||
}
|
||||
|
||||
__set_current_state(TASK_RUNNING);
|
||||
return sem;
|
||||
out_nolock:
|
||||
list_del(&waiter.list);
|
||||
if (list_empty(&sem->wait_list))
|
||||
atomic_long_add(-RWSEM_WAITING_BIAS, &sem->count);
|
||||
raw_spin_unlock_irq(&sem->wait_lock);
|
||||
__set_current_state(TASK_RUNNING);
|
||||
return ERR_PTR(-EINTR);
|
||||
}
|
||||
|
||||
__visible struct rw_semaphore * __sched
|
||||
rwsem_down_read_failed(struct rw_semaphore *sem)
|
||||
{
|
||||
return __rwsem_down_read_failed_common(sem, TASK_UNINTERRUPTIBLE);
|
||||
}
|
||||
EXPORT_SYMBOL(rwsem_down_read_failed);
|
||||
|
||||
__visible struct rw_semaphore * __sched
|
||||
rwsem_down_read_failed_killable(struct rw_semaphore *sem)
|
||||
{
|
||||
return __rwsem_down_read_failed_common(sem, TASK_KILLABLE);
|
||||
}
|
||||
EXPORT_SYMBOL(rwsem_down_read_failed_killable);
|
||||
|
||||
/*
|
||||
* This function must be called with the sem->wait_lock held to prevent
|
||||
* race conditions between checking the rwsem wait list and setting the
|
||||
|
Reference in New Issue
Block a user