Skip to content

Commit cb42c9a

Browse files
mflemingIngo Molnar
authored andcommitted
sched/core: Add debugging code to catch missing update_rq_clock() calls
There's no diagnostic checks for figuring out when we've accidentally missed update_rq_clock() calls. Let's add some by piggybacking on the rq_*pin_lock() wrappers. The idea behind the diagnostic checks is that upon pining rq lock the rq clock should be updated, via update_rq_clock(), before anybody reads the clock with rq_clock() or rq_clock_task(). The exception to this rule is when updates have explicitly been disabled with the rq_clock_skip_update() optimisation. There are some functions that only unpin the rq lock in order to grab some other lock and avoid deadlock. In that case we don't need to update the clock again and the previous diagnostic state can be carried over in rq_repin_lock() by saving the state in the rq_flags context. Since this patch adds a new clock update flag and some already exist in rq::clock_skip_update, that field has now been renamed. An attempt has been made to keep the flag manipulation code small and fast since it's used in the heart of the __schedule() fast path. For the !CONFIG_SCHED_DEBUG case the only object code change (other than addresses) is the following change to reset RQCF_ACT_SKIP inside of __schedule(), - c7 83 38 09 00 00 00 movl $0x0,0x938(%rbx) - 00 00 00 + 83 a3 38 09 00 00 fc andl $0xfffffffc,0x938(%rbx) Suggested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Byungchul Park <byungchul.park@lge.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Jan Kara <jack@suse.cz> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Luca Abeni <luca.abeni@unitn.it> Cc: Mel Gorman <mgorman@techsingularity.net> Cc: Mike Galbraith <efault@gmx.de> Cc: Mike Galbraith <umgwanakikbuti@gmail.com> Cc: Petr Mladek <pmladek@suse.com> Cc: Rik van Riel <riel@redhat.com> Cc: Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Wanpeng Li <wanpeng.li@hotmail.com> Cc: Yuyang Du <yuyang.du@intel.com> Link: http://lkml.kernel.org/r/20160921133813.31976-8-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent 2fb8d36 commit cb42c9a

File tree

2 files changed

+75
-10
lines changed

2 files changed

+75
-10
lines changed

kernel/sched/core.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,12 @@ void update_rq_clock(struct rq *rq)
102102

103103
lockdep_assert_held(&rq->lock);
104104

105-
if (rq->clock_skip_update & RQCF_ACT_SKIP)
105+
if (rq->clock_update_flags & RQCF_ACT_SKIP)
106106
return;
107107

108+
#ifdef CONFIG_SCHED_DEBUG
109+
rq->clock_update_flags |= RQCF_UPDATED;
110+
#endif
108111
delta = sched_clock_cpu(cpu_of(rq)) - rq->clock;
109112
if (delta < 0)
110113
return;
@@ -2889,7 +2892,7 @@ context_switch(struct rq *rq, struct task_struct *prev,
28892892
rq->prev_mm = oldmm;
28902893
}
28912894

2892-
rq->clock_skip_update = 0;
2895+
rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
28932896

28942897
/*
28952898
* Since the runqueue lock will be released by the next
@@ -3364,7 +3367,7 @@ static void __sched notrace __schedule(bool preempt)
33643367
raw_spin_lock(&rq->lock);
33653368
rq_pin_lock(rq, &rf);
33663369

3367-
rq->clock_skip_update <<= 1; /* promote REQ to ACT */
3370+
rq->clock_update_flags <<= 1; /* promote REQ to ACT */
33683371

33693372
switch_count = &prev->nivcsw;
33703373
if (!preempt && prev->state) {
@@ -3405,7 +3408,7 @@ static void __sched notrace __schedule(bool preempt)
34053408
trace_sched_switch(preempt, prev, next);
34063409
rq = context_switch(rq, prev, next, &rf); /* unlocks the rq */
34073410
} else {
3408-
rq->clock_skip_update = 0;
3411+
rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
34093412
rq_unpin_lock(rq, &rf);
34103413
raw_spin_unlock_irq(&rq->lock);
34113414
}

kernel/sched/sched.h

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ struct rq {
644644
unsigned long next_balance;
645645
struct mm_struct *prev_mm;
646646

647-
unsigned int clock_skip_update;
647+
unsigned int clock_update_flags;
648648
u64 clock;
649649
u64 clock_task;
650650

@@ -768,48 +768,110 @@ static inline u64 __rq_clock_broken(struct rq *rq)
768768
return READ_ONCE(rq->clock);
769769
}
770770

771+
/*
772+
* rq::clock_update_flags bits
773+
*
774+
* %RQCF_REQ_SKIP - will request skipping of clock update on the next
775+
* call to __schedule(). This is an optimisation to avoid
776+
* neighbouring rq clock updates.
777+
*
778+
* %RQCF_ACT_SKIP - is set from inside of __schedule() when skipping is
779+
* in effect and calls to update_rq_clock() are being ignored.
780+
*
781+
* %RQCF_UPDATED - is a debug flag that indicates whether a call has been
782+
* made to update_rq_clock() since the last time rq::lock was pinned.
783+
*
784+
* If inside of __schedule(), clock_update_flags will have been
785+
* shifted left (a left shift is a cheap operation for the fast path
786+
* to promote %RQCF_REQ_SKIP to %RQCF_ACT_SKIP), so you must use,
787+
*
788+
* if (rq-clock_update_flags >= RQCF_UPDATED)
789+
*
790+
* to check if %RQCF_UPADTED is set. It'll never be shifted more than
791+
* one position though, because the next rq_unpin_lock() will shift it
792+
* back.
793+
*/
794+
#define RQCF_REQ_SKIP 0x01
795+
#define RQCF_ACT_SKIP 0x02
796+
#define RQCF_UPDATED 0x04
797+
798+
static inline void assert_clock_updated(struct rq *rq)
799+
{
800+
/*
801+
* The only reason for not seeing a clock update since the
802+
* last rq_pin_lock() is if we're currently skipping updates.
803+
*/
804+
SCHED_WARN_ON(rq->clock_update_flags < RQCF_ACT_SKIP);
805+
}
806+
771807
static inline u64 rq_clock(struct rq *rq)
772808
{
773809
lockdep_assert_held(&rq->lock);
810+
assert_clock_updated(rq);
811+
774812
return rq->clock;
775813
}
776814

777815
static inline u64 rq_clock_task(struct rq *rq)
778816
{
779817
lockdep_assert_held(&rq->lock);
818+
assert_clock_updated(rq);
819+
780820
return rq->clock_task;
781821
}
782822

783-
#define RQCF_REQ_SKIP 0x01
784-
#define RQCF_ACT_SKIP 0x02
785-
786823
static inline void rq_clock_skip_update(struct rq *rq, bool skip)
787824
{
788825
lockdep_assert_held(&rq->lock);
789826
if (skip)
790-
rq->clock_skip_update |= RQCF_REQ_SKIP;
827+
rq->clock_update_flags |= RQCF_REQ_SKIP;
791828
else
792-
rq->clock_skip_update &= ~RQCF_REQ_SKIP;
829+
rq->clock_update_flags &= ~RQCF_REQ_SKIP;
793830
}
794831

795832
struct rq_flags {
796833
unsigned long flags;
797834
struct pin_cookie cookie;
835+
#ifdef CONFIG_SCHED_DEBUG
836+
/*
837+
* A copy of (rq::clock_update_flags & RQCF_UPDATED) for the
838+
* current pin context is stashed here in case it needs to be
839+
* restored in rq_repin_lock().
840+
*/
841+
unsigned int clock_update_flags;
842+
#endif
798843
};
799844

800845
static inline void rq_pin_lock(struct rq *rq, struct rq_flags *rf)
801846
{
802847
rf->cookie = lockdep_pin_lock(&rq->lock);
848+
849+
#ifdef CONFIG_SCHED_DEBUG
850+
rq->clock_update_flags &= (RQCF_REQ_SKIP|RQCF_ACT_SKIP);
851+
rf->clock_update_flags = 0;
852+
#endif
803853
}
804854

805855
static inline void rq_unpin_lock(struct rq *rq, struct rq_flags *rf)
806856
{
857+
#ifdef CONFIG_SCHED_DEBUG
858+
if (rq->clock_update_flags > RQCF_ACT_SKIP)
859+
rf->clock_update_flags = RQCF_UPDATED;
860+
#endif
861+
807862
lockdep_unpin_lock(&rq->lock, rf->cookie);
808863
}
809864

810865
static inline void rq_repin_lock(struct rq *rq, struct rq_flags *rf)
811866
{
812867
lockdep_repin_lock(&rq->lock, rf->cookie);
868+
869+
#ifdef CONFIG_SCHED_DEBUG
870+
/*
871+
* Restore the value we stashed in @rf for this pin context.
872+
*/
873+
rq->clock_update_flags |= rf->clock_update_flags;
874+
#endif
813875
}
814876

815877
#ifdef CONFIG_NUMA

0 commit comments

Comments
 (0)