diff options
| -rw-r--r-- | kernel/time/sched_clock.c | 27 |
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 | ||
| 18 | struct clock_data { | 19 | struct 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) | |||
| 195 | static void sched_clock_resume(void) | 185 | static 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 | ||
