Commit ee042be1 authored by Namhyung Kim's avatar Namhyung Kim Committed by Peter Zijlstra

locking: Apply contention tracepoints in the slow path

Adding the lock contention tracepoints in various lock function slow
paths.  Note that each arch can define spinlock differently, I only
added it only to the generic qspinlock for now.
Signed-off-by: default avatarNamhyung Kim <namhyung@kernel.org>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: default avatarHyeonggon Yoo <42.hyeyoo@gmail.com>
Link: https://lkml.kernel.org/r/20220322185709.141236-3-namhyung@kernel.org
parent 16edd9b5
...@@ -644,6 +644,7 @@ __mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclas ...@@ -644,6 +644,7 @@ __mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclas
} }
set_current_state(state); set_current_state(state);
trace_contention_begin(lock, 0);
for (;;) { for (;;) {
bool first; bool first;
...@@ -710,6 +711,7 @@ __mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclas ...@@ -710,6 +711,7 @@ __mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclas
skip_wait: skip_wait:
/* got the lock - cleanup and rejoice! */ /* got the lock - cleanup and rejoice! */
lock_acquired(&lock->dep_map, ip); lock_acquired(&lock->dep_map, ip);
trace_contention_end(lock, 0);
if (ww_ctx) if (ww_ctx)
ww_mutex_lock_acquired(ww, ww_ctx); ww_mutex_lock_acquired(ww, ww_ctx);
...@@ -721,6 +723,7 @@ __mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclas ...@@ -721,6 +723,7 @@ __mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclas
err: err:
__set_current_state(TASK_RUNNING); __set_current_state(TASK_RUNNING);
__mutex_remove_waiter(lock, &waiter); __mutex_remove_waiter(lock, &waiter);
trace_contention_end(lock, ret);
err_early_kill: err_early_kill:
raw_spin_unlock(&lock->wait_lock); raw_spin_unlock(&lock->wait_lock);
debug_mutex_free_waiter(&waiter); debug_mutex_free_waiter(&waiter);
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/sched/task.h> #include <linux/sched/task.h>
#include <linux/sched/debug.h> #include <linux/sched/debug.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <trace/events/lock.h>
int __percpu_init_rwsem(struct percpu_rw_semaphore *sem, int __percpu_init_rwsem(struct percpu_rw_semaphore *sem,
const char *name, struct lock_class_key *key) const char *name, struct lock_class_key *key)
...@@ -171,9 +172,11 @@ bool __sched __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) ...@@ -171,9 +172,11 @@ bool __sched __percpu_down_read(struct percpu_rw_semaphore *sem, bool try)
if (try) if (try)
return false; return false;
trace_contention_begin(sem, LCB_F_PERCPU | LCB_F_READ);
preempt_enable(); preempt_enable();
percpu_rwsem_wait(sem, /* .reader = */ true); percpu_rwsem_wait(sem, /* .reader = */ true);
preempt_disable(); preempt_disable();
trace_contention_end(sem, 0);
return true; return true;
} }
...@@ -216,6 +219,7 @@ void __sched percpu_down_write(struct percpu_rw_semaphore *sem) ...@@ -216,6 +219,7 @@ void __sched percpu_down_write(struct percpu_rw_semaphore *sem)
{ {
might_sleep(); might_sleep();
rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_); rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_);
trace_contention_begin(sem, LCB_F_PERCPU | LCB_F_WRITE);
/* Notify readers to take the slow path. */ /* Notify readers to take the slow path. */
rcu_sync_enter(&sem->rss); rcu_sync_enter(&sem->rss);
...@@ -237,6 +241,7 @@ void __sched percpu_down_write(struct percpu_rw_semaphore *sem) ...@@ -237,6 +241,7 @@ void __sched percpu_down_write(struct percpu_rw_semaphore *sem)
/* Wait for all active readers to complete. */ /* Wait for all active readers to complete. */
rcuwait_wait_event(&sem->writer, readers_active_check(sem), TASK_UNINTERRUPTIBLE); rcuwait_wait_event(&sem->writer, readers_active_check(sem), TASK_UNINTERRUPTIBLE);
trace_contention_end(sem, 0);
} }
EXPORT_SYMBOL_GPL(percpu_down_write); EXPORT_SYMBOL_GPL(percpu_down_write);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/hardirq.h> #include <linux/hardirq.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <trace/events/lock.h>
/** /**
* queued_read_lock_slowpath - acquire read lock of a queue rwlock * queued_read_lock_slowpath - acquire read lock of a queue rwlock
...@@ -34,6 +35,8 @@ void queued_read_lock_slowpath(struct qrwlock *lock) ...@@ -34,6 +35,8 @@ void queued_read_lock_slowpath(struct qrwlock *lock)
} }
atomic_sub(_QR_BIAS, &lock->cnts); atomic_sub(_QR_BIAS, &lock->cnts);
trace_contention_begin(lock, LCB_F_SPIN | LCB_F_READ);
/* /*
* Put the reader into the wait queue * Put the reader into the wait queue
*/ */
...@@ -51,6 +54,8 @@ void queued_read_lock_slowpath(struct qrwlock *lock) ...@@ -51,6 +54,8 @@ void queued_read_lock_slowpath(struct qrwlock *lock)
* Signal the next one in queue to become queue head * Signal the next one in queue to become queue head
*/ */
arch_spin_unlock(&lock->wait_lock); arch_spin_unlock(&lock->wait_lock);
trace_contention_end(lock, 0);
} }
EXPORT_SYMBOL(queued_read_lock_slowpath); EXPORT_SYMBOL(queued_read_lock_slowpath);
...@@ -62,6 +67,8 @@ void queued_write_lock_slowpath(struct qrwlock *lock) ...@@ -62,6 +67,8 @@ void queued_write_lock_slowpath(struct qrwlock *lock)
{ {
int cnts; int cnts;
trace_contention_begin(lock, LCB_F_SPIN | LCB_F_WRITE);
/* Put the writer into the wait queue */ /* Put the writer into the wait queue */
arch_spin_lock(&lock->wait_lock); arch_spin_lock(&lock->wait_lock);
...@@ -79,5 +86,7 @@ void queued_write_lock_slowpath(struct qrwlock *lock) ...@@ -79,5 +86,7 @@ void queued_write_lock_slowpath(struct qrwlock *lock)
} while (!atomic_try_cmpxchg_acquire(&lock->cnts, &cnts, _QW_LOCKED)); } while (!atomic_try_cmpxchg_acquire(&lock->cnts, &cnts, _QW_LOCKED));
unlock: unlock:
arch_spin_unlock(&lock->wait_lock); arch_spin_unlock(&lock->wait_lock);
trace_contention_end(lock, 0);
} }
EXPORT_SYMBOL(queued_write_lock_slowpath); EXPORT_SYMBOL(queued_write_lock_slowpath);
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/prefetch.h> #include <linux/prefetch.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/qspinlock.h> #include <asm/qspinlock.h>
#include <trace/events/lock.h>
/* /*
* Include queued spinlock statistics code * Include queued spinlock statistics code
...@@ -401,6 +402,8 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val) ...@@ -401,6 +402,8 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
idx = node->count++; idx = node->count++;
tail = encode_tail(smp_processor_id(), idx); tail = encode_tail(smp_processor_id(), idx);
trace_contention_begin(lock, LCB_F_SPIN);
/* /*
* 4 nodes are allocated based on the assumption that there will * 4 nodes are allocated based on the assumption that there will
* not be nested NMIs taking spinlocks. That may not be true in * not be nested NMIs taking spinlocks. That may not be true in
...@@ -554,6 +557,8 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val) ...@@ -554,6 +557,8 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
pv_kick_node(lock, next); pv_kick_node(lock, next);
release: release:
trace_contention_end(lock, 0);
/* /*
* release the node * release the node
*/ */
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#include <linux/sched/wake_q.h> #include <linux/sched/wake_q.h>
#include <linux/ww_mutex.h> #include <linux/ww_mutex.h>
#include <trace/events/lock.h>
#include "rtmutex_common.h" #include "rtmutex_common.h"
#ifndef WW_RT #ifndef WW_RT
...@@ -1579,6 +1581,8 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock, ...@@ -1579,6 +1581,8 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock,
set_current_state(state); set_current_state(state);
trace_contention_begin(lock, LCB_F_RT);
ret = task_blocks_on_rt_mutex(lock, waiter, current, ww_ctx, chwalk); ret = task_blocks_on_rt_mutex(lock, waiter, current, ww_ctx, chwalk);
if (likely(!ret)) if (likely(!ret))
ret = rt_mutex_slowlock_block(lock, ww_ctx, state, NULL, waiter); ret = rt_mutex_slowlock_block(lock, ww_ctx, state, NULL, waiter);
...@@ -1601,6 +1605,9 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock, ...@@ -1601,6 +1605,9 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock,
* unconditionally. We might have to fix that up. * unconditionally. We might have to fix that up.
*/ */
fixup_rt_mutex_waiters(lock); fixup_rt_mutex_waiters(lock);
trace_contention_end(lock, ret);
return ret; return ret;
} }
...@@ -1683,6 +1690,8 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock) ...@@ -1683,6 +1690,8 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock)
/* Save current state and set state to TASK_RTLOCK_WAIT */ /* Save current state and set state to TASK_RTLOCK_WAIT */
current_save_and_set_rtlock_wait_state(); current_save_and_set_rtlock_wait_state();
trace_contention_begin(lock, LCB_F_RT);
task_blocks_on_rt_mutex(lock, &waiter, current, NULL, RT_MUTEX_MIN_CHAINWALK); task_blocks_on_rt_mutex(lock, &waiter, current, NULL, RT_MUTEX_MIN_CHAINWALK);
for (;;) { for (;;) {
...@@ -1712,6 +1721,8 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock) ...@@ -1712,6 +1721,8 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock)
*/ */
fixup_rt_mutex_waiters(lock); fixup_rt_mutex_waiters(lock);
debug_rt_mutex_free_waiter(&waiter); debug_rt_mutex_free_waiter(&waiter);
trace_contention_end(lock, 0);
} }
static __always_inline void __sched rtlock_slowlock(struct rt_mutex_base *lock) static __always_inline void __sched rtlock_slowlock(struct rt_mutex_base *lock)
......
...@@ -112,6 +112,8 @@ static int __sched __rwbase_read_lock(struct rwbase_rt *rwb, ...@@ -112,6 +112,8 @@ static int __sched __rwbase_read_lock(struct rwbase_rt *rwb,
* Reader2 to call up_read(), which might be unbound. * Reader2 to call up_read(), which might be unbound.
*/ */
trace_contention_begin(rwb, LCB_F_RT | LCB_F_READ);
/* /*
* For rwlocks this returns 0 unconditionally, so the below * For rwlocks this returns 0 unconditionally, so the below
* !ret conditionals are optimized out. * !ret conditionals are optimized out.
...@@ -130,6 +132,8 @@ static int __sched __rwbase_read_lock(struct rwbase_rt *rwb, ...@@ -130,6 +132,8 @@ static int __sched __rwbase_read_lock(struct rwbase_rt *rwb,
raw_spin_unlock_irq(&rtm->wait_lock); raw_spin_unlock_irq(&rtm->wait_lock);
if (!ret) if (!ret)
rwbase_rtmutex_unlock(rtm); rwbase_rtmutex_unlock(rtm);
trace_contention_end(rwb, ret);
return ret; return ret;
} }
...@@ -247,11 +251,13 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb, ...@@ -247,11 +251,13 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb,
goto out_unlock; goto out_unlock;
rwbase_set_and_save_current_state(state); rwbase_set_and_save_current_state(state);
trace_contention_begin(rwb, LCB_F_RT | LCB_F_WRITE);
for (;;) { for (;;) {
/* Optimized out for rwlocks */ /* Optimized out for rwlocks */
if (rwbase_signal_pending_state(state, current)) { if (rwbase_signal_pending_state(state, current)) {
rwbase_restore_current_state(); rwbase_restore_current_state();
__rwbase_write_unlock(rwb, 0, flags); __rwbase_write_unlock(rwb, 0, flags);
trace_contention_end(rwb, -EINTR);
return -EINTR; return -EINTR;
} }
...@@ -265,6 +271,7 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb, ...@@ -265,6 +271,7 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb,
set_current_state(state); set_current_state(state);
} }
rwbase_restore_current_state(); rwbase_restore_current_state();
trace_contention_end(rwb, 0);
out_unlock: out_unlock:
raw_spin_unlock_irqrestore(&rtm->wait_lock, flags); raw_spin_unlock_irqrestore(&rtm->wait_lock, flags);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <trace/events/lock.h>
#ifndef CONFIG_PREEMPT_RT #ifndef CONFIG_PREEMPT_RT
#include "lock_events.h" #include "lock_events.h"
...@@ -1056,6 +1057,8 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, long count, unsigned int stat ...@@ -1056,6 +1057,8 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, long count, unsigned int stat
if (!wake_q_empty(&wake_q)) if (!wake_q_empty(&wake_q))
wake_up_q(&wake_q); wake_up_q(&wake_q);
trace_contention_begin(sem, LCB_F_READ);
/* wait to be given the lock */ /* wait to be given the lock */
for (;;) { for (;;) {
set_current_state(state); set_current_state(state);
...@@ -1077,12 +1080,14 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, long count, unsigned int stat ...@@ -1077,12 +1080,14 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, long count, unsigned int stat
__set_current_state(TASK_RUNNING); __set_current_state(TASK_RUNNING);
lockevent_inc(rwsem_rlock); lockevent_inc(rwsem_rlock);
trace_contention_end(sem, 0);
return sem; return sem;
out_nolock: out_nolock:
rwsem_del_wake_waiter(sem, &waiter, &wake_q); rwsem_del_wake_waiter(sem, &waiter, &wake_q);
__set_current_state(TASK_RUNNING); __set_current_state(TASK_RUNNING);
lockevent_inc(rwsem_rlock_fail); lockevent_inc(rwsem_rlock_fail);
trace_contention_end(sem, -EINTR);
return ERR_PTR(-EINTR); return ERR_PTR(-EINTR);
} }
...@@ -1132,6 +1137,8 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state) ...@@ -1132,6 +1137,8 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
/* wait until we successfully acquire the lock */ /* wait until we successfully acquire the lock */
set_current_state(state); set_current_state(state);
trace_contention_begin(sem, LCB_F_WRITE);
for (;;) { for (;;) {
if (rwsem_try_write_lock(sem, &waiter)) { if (rwsem_try_write_lock(sem, &waiter)) {
/* rwsem_try_write_lock() implies ACQUIRE on success */ /* rwsem_try_write_lock() implies ACQUIRE on success */
...@@ -1171,6 +1178,7 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state) ...@@ -1171,6 +1178,7 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
__set_current_state(TASK_RUNNING); __set_current_state(TASK_RUNNING);
raw_spin_unlock_irq(&sem->wait_lock); raw_spin_unlock_irq(&sem->wait_lock);
lockevent_inc(rwsem_wlock); lockevent_inc(rwsem_wlock);
trace_contention_end(sem, 0);
return sem; return sem;
out_nolock: out_nolock:
...@@ -1178,6 +1186,7 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state) ...@@ -1178,6 +1186,7 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
raw_spin_lock_irq(&sem->wait_lock); raw_spin_lock_irq(&sem->wait_lock);
rwsem_del_wake_waiter(sem, &waiter, &wake_q); rwsem_del_wake_waiter(sem, &waiter, &wake_q);
lockevent_inc(rwsem_wlock_fail); lockevent_inc(rwsem_wlock_fail);
trace_contention_end(sem, -EINTR);
return ERR_PTR(-EINTR); return ERR_PTR(-EINTR);
} }
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/semaphore.h> #include <linux/semaphore.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <trace/events/lock.h>
static noinline void __down(struct semaphore *sem); static noinline void __down(struct semaphore *sem);
static noinline int __down_interruptible(struct semaphore *sem); static noinline int __down_interruptible(struct semaphore *sem);
...@@ -205,7 +206,7 @@ struct semaphore_waiter { ...@@ -205,7 +206,7 @@ struct semaphore_waiter {
* constant, and thus optimised away by the compiler. Likewise the * constant, and thus optimised away by the compiler. Likewise the
* 'timeout' parameter for the cases without timeouts. * 'timeout' parameter for the cases without timeouts.
*/ */
static inline int __sched __down_common(struct semaphore *sem, long state, static inline int __sched ___down_common(struct semaphore *sem, long state,
long timeout) long timeout)
{ {
struct semaphore_waiter waiter; struct semaphore_waiter waiter;
...@@ -236,6 +237,18 @@ static inline int __sched __down_common(struct semaphore *sem, long state, ...@@ -236,6 +237,18 @@ static inline int __sched __down_common(struct semaphore *sem, long state,
return -EINTR; return -EINTR;
} }
static inline int __sched __down_common(struct semaphore *sem, long state,
long timeout)
{
int ret;
trace_contention_begin(sem, 0);
ret = ___down_common(sem, state, timeout);
trace_contention_end(sem, ret);
return ret;
}
static noinline void __sched __down(struct semaphore *sem) static noinline void __sched __down(struct semaphore *sem)
{ {
__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment