aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time/sched_clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/time/sched_clock.c')
-rw-r--r--kernel/time/sched_clock.c27
1 files changed, 8 insertions, 19 deletions
diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c
index a326f27d7f09..396f7b9dccc9 100644
--- a/kernel/time/sched_clock.c
+++ b/kernel/time/sched_clock.c
@@ -14,11 +14,12 @@
14#include <linux/syscore_ops.h> 14#include <linux/syscore_ops.h>
15#include <linux/timer.h> 15#include <linux/timer.h>
16#include <linux/sched_clock.h> 16#include <linux/sched_clock.h>
17#include <linux/seqlock.h>
17 18
18struct clock_data { 19struct clock_data {
19 u64 epoch_ns; 20 u64 epoch_ns;
20 u32 epoch_cyc; 21 u32 epoch_cyc;
21 u32 epoch_cyc_copy; 22 seqcount_t seq;
22 unsigned long rate; 23 unsigned long rate;
23 u32 mult; 24 u32 mult;
24 u32 shift; 25 u32 shift;
@@ -54,23 +55,16 @@ static unsigned long long notrace sched_clock_32(void)
54 u64 epoch_ns; 55 u64 epoch_ns;
55 u32 epoch_cyc; 56 u32 epoch_cyc;
56 u32 cyc; 57 u32 cyc;
58 unsigned long seq;
57 59
58 if (cd.suspended) 60 if (cd.suspended)
59 return cd.epoch_ns; 61 return cd.epoch_ns;
60 62
61 /*
62 * Load the epoch_cyc and epoch_ns atomically. We do this by
63 * ensuring that we always write epoch_cyc, epoch_ns and
64 * epoch_cyc_copy in strict order, and read them in strict order.
65 * If epoch_cyc and epoch_cyc_copy are not equal, then we're in
66 * the middle of an update, and we should repeat the load.
67 */
68 do { 63 do {
64 seq = read_seqcount_begin(&cd.seq);
69 epoch_cyc = cd.epoch_cyc; 65 epoch_cyc = cd.epoch_cyc;
70 smp_rmb();
71 epoch_ns = cd.epoch_ns; 66 epoch_ns = cd.epoch_ns;
72 smp_rmb(); 67 } while (read_seqcount_retry(&cd.seq, seq));
73 } while (epoch_cyc != cd.epoch_cyc_copy);
74 68
75 cyc = read_sched_clock(); 69 cyc = read_sched_clock();
76 cyc = (cyc - epoch_cyc) & sched_clock_mask; 70 cyc = (cyc - epoch_cyc) & sched_clock_mask;
@@ -90,16 +84,12 @@ static void notrace update_sched_clock(void)
90 ns = cd.epoch_ns + 84 ns = cd.epoch_ns +
91 cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask, 85 cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask,
92 cd.mult, cd.shift); 86 cd.mult, cd.shift);
93 /* 87
94 * Write epoch_cyc and epoch_ns in a way that the update is
95 * detectable in cyc_to_fixed_sched_clock().
96 */
97 raw_local_irq_save(flags); 88 raw_local_irq_save(flags);
98 cd.epoch_cyc_copy = cyc; 89 write_seqcount_begin(&cd.seq);
99 smp_wmb();
100 cd.epoch_ns = ns; 90 cd.epoch_ns = ns;
101 smp_wmb();
102 cd.epoch_cyc = cyc; 91 cd.epoch_cyc = cyc;
92 write_seqcount_end(&cd.seq);
103 raw_local_irq_restore(flags); 93 raw_local_irq_restore(flags);
104} 94}
105 95
@@ -195,7 +185,6 @@ static int sched_clock_suspend(void)
195static void sched_clock_resume(void) 185static void sched_clock_resume(void)
196{ 186{
197 cd.epoch_cyc = read_sched_clock(); 187 cd.epoch_cyc = read_sched_clock();
198 cd.epoch_cyc_copy = cd.epoch_cyc;
199 cd.suspended = false; 188 cd.suspended = false;
200} 189}
201 190