sched_clock: Use seqcount instead of rolling our own
Stephen Boyd [Thu, 18 Jul 2013 23:21:15 +0000 (16:21 -0700)]
We're going to increase the cyc value to 64 bits in the near
future. Doing that is going to break the custom seqcount
implementation in the sched_clock code because 64 bit numbers
aren't guaranteed to be atomic. Replace the cyc_copy with a
seqcount to avoid this problem.

Bug 1399318

Cc: Russell King <linux@arm.linux.org.uk>
Acked-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: John Stultz <john.stultz@linaro.org>
(cherry picked from commit 85c3d2dd15be4d577a37ffb8bbbd019fc8e3280a)
Signed-off-by: Ajay Nandakumar <anandakumarm@nvidia.com>
Change-Id: Iff7ec4aca829452c5b51576dae9437a37ed8f619

kernel/time/sched_clock.c

index 0b479a6..965fa22 100644 (file)
 #include <linux/syscore_ops.h>
 #include <linux/timer.h>
 #include <linux/sched_clock.h>
+#include <linux/seqlock.h>
 
 struct clock_data {
        u64 epoch_ns;
        u32 epoch_cyc;
-       u32 epoch_cyc_copy;
+       seqcount_t seq;
        unsigned long rate;
        u32 mult;
        u32 shift;
@@ -54,23 +55,16 @@ static unsigned long long notrace sched_clock_32(void)
        u64 epoch_ns;
        u32 epoch_cyc;
        u32 cyc;
+       unsigned long seq;
 
        if (cd.suspended)
                return cd.epoch_ns;
 
-       /*
-        * Load the epoch_cyc and epoch_ns atomically.  We do this by
-        * ensuring that we always write epoch_cyc, epoch_ns and
-        * epoch_cyc_copy in strict order, and read them in strict order.
-        * If epoch_cyc and epoch_cyc_copy are not equal, then we're in
-        * the middle of an update, and we should repeat the load.
-        */
        do {
+               seq = read_seqcount_begin(&cd.seq);
                epoch_cyc = cd.epoch_cyc;
-               smp_rmb();
                epoch_ns = cd.epoch_ns;
-               smp_rmb();
-       } while (epoch_cyc != cd.epoch_cyc_copy);
+       } while (read_seqcount_retry(&cd.seq, seq));
 
        cyc = read_sched_clock();
        cyc = (cyc - epoch_cyc) & sched_clock_mask;
@@ -90,16 +84,12 @@ static void notrace update_sched_clock(void)
        ns = cd.epoch_ns +
                cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask,
                          cd.mult, cd.shift);
-       /*
-        * Write epoch_cyc and epoch_ns in a way that the update is
-        * detectable in cyc_to_fixed_sched_clock().
-        */
+
        raw_local_irq_save(flags);
-       cd.epoch_cyc_copy = cyc;
-       smp_wmb();
+       write_seqcount_begin(&cd.seq);
        cd.epoch_ns = ns;
-       smp_wmb();
        cd.epoch_cyc = cyc;
+       write_seqcount_end(&cd.seq);
        raw_local_irq_restore(flags);
 }
 
@@ -195,7 +185,6 @@ static int sched_clock_suspend(void)
 static void sched_clock_resume(void)
 {
        cd.epoch_cyc = read_sched_clock();
-       cd.epoch_cyc_copy = cd.epoch_cyc;
        cd.suspended = false;
 }