Skip to content

Commit 2f0778a

Browse files
Marc ZyngierRussell King
Marc Zyngier
authored and
Russell King
committed
ARM: 7205/2: sched_clock: allow sched_clock to be selected at runtime
sched_clock() is yet another blocker on the road to the single image. This patch implements an idea by Russell King: http://www.spinics.net/lists/linux-omap/msg49561.html Instead of asking the platform to implement both sched_clock() itself and the rollover callback, simply register a read() function, and let the ARM code care about sched_clock() itself, the conversion to ns and the rollover. sched_clock() uses this read() function as an indirection to the platform code. If the platform doesn't provide a read(), the code falls back to the jiffy counter (just like the default sched_clock). This allow some simplifications and possibly some footprint gain when multiple platforms are compiled in. Among the drawbacks, the removal of the *_fixed_sched_clock optimization which could negatively impact some platforms (sa1100, tegra, versatile and omap). Tested on 11MPCore, OMAP4 and Tegra. Cc: Imre Kaloz <[email protected]> Cc: Eric Miao <[email protected]> Cc: Colin Cross <[email protected]> Cc: Erik Gilling <[email protected]> Cc: Olof Johansson <[email protected]> Cc: Sascha Hauer <[email protected]> Cc: Alessandro Rubini <[email protected]> Cc: STEricsson <[email protected]> Cc: Lennert Buytenhek <[email protected]> Cc: Ben Dooks <[email protected]> Tested-by: Jamie Iles <[email protected]> Tested-by: Tony Lindgren <[email protected]> Tested-by: Kyungmin Park <[email protected]> Acked-by: Linus Walleij <[email protected]> Acked-by: Nicolas Pitre <[email protected]> Acked-by: Krzysztof Halasa <[email protected]> Acked-by: Kukjin Kim <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Signed-off-by: Russell King <[email protected]>
1 parent 3bdc348 commit 2f0778a

File tree

19 files changed

+161
-435
lines changed

19 files changed

+161
-435
lines changed

arch/arm/include/asm/sched_clock.h

Lines changed: 1 addition & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -8,113 +8,7 @@
88
#ifndef ASM_SCHED_CLOCK
99
#define ASM_SCHED_CLOCK
1010

11-
#include <linux/kernel.h>
12-
#include <linux/types.h>
13-
14-
struct clock_data {
15-
u64 epoch_ns;
16-
u32 epoch_cyc;
17-
u32 epoch_cyc_copy;
18-
u32 mult;
19-
u32 shift;
20-
};
21-
22-
#define DEFINE_CLOCK_DATA(name) struct clock_data name
23-
24-
static inline u64 cyc_to_ns(u64 cyc, u32 mult, u32 shift)
25-
{
26-
return (cyc * mult) >> shift;
27-
}
28-
29-
/*
30-
* Atomically update the sched_clock epoch. Your update callback will
31-
* be called from a timer before the counter wraps - read the current
32-
* counter value, and call this function to safely move the epochs
33-
* forward. Only use this from the update callback.
34-
*/
35-
static inline void update_sched_clock(struct clock_data *cd, u32 cyc, u32 mask)
36-
{
37-
unsigned long flags;
38-
u64 ns = cd->epoch_ns +
39-
cyc_to_ns((cyc - cd->epoch_cyc) & mask, cd->mult, cd->shift);
40-
41-
/*
42-
* Write epoch_cyc and epoch_ns in a way that the update is
43-
* detectable in cyc_to_fixed_sched_clock().
44-
*/
45-
raw_local_irq_save(flags);
46-
cd->epoch_cyc = cyc;
47-
smp_wmb();
48-
cd->epoch_ns = ns;
49-
smp_wmb();
50-
cd->epoch_cyc_copy = cyc;
51-
raw_local_irq_restore(flags);
52-
}
53-
54-
/*
55-
* If your clock rate is known at compile time, using this will allow
56-
* you to optimize the mult/shift loads away. This is paired with
57-
* init_fixed_sched_clock() to ensure that your mult/shift are correct.
58-
*/
59-
static inline unsigned long long cyc_to_fixed_sched_clock(struct clock_data *cd,
60-
u32 cyc, u32 mask, u32 mult, u32 shift)
61-
{
62-
u64 epoch_ns;
63-
u32 epoch_cyc;
64-
65-
/*
66-
* Load the epoch_cyc and epoch_ns atomically. We do this by
67-
* ensuring that we always write epoch_cyc, epoch_ns and
68-
* epoch_cyc_copy in strict order, and read them in strict order.
69-
* If epoch_cyc and epoch_cyc_copy are not equal, then we're in
70-
* the middle of an update, and we should repeat the load.
71-
*/
72-
do {
73-
epoch_cyc = cd->epoch_cyc;
74-
smp_rmb();
75-
epoch_ns = cd->epoch_ns;
76-
smp_rmb();
77-
} while (epoch_cyc != cd->epoch_cyc_copy);
78-
79-
return epoch_ns + cyc_to_ns((cyc - epoch_cyc) & mask, mult, shift);
80-
}
81-
82-
/*
83-
* Otherwise, you need to use this, which will obtain the mult/shift
84-
* from the clock_data structure. Use init_sched_clock() with this.
85-
*/
86-
static inline unsigned long long cyc_to_sched_clock(struct clock_data *cd,
87-
u32 cyc, u32 mask)
88-
{
89-
return cyc_to_fixed_sched_clock(cd, cyc, mask, cd->mult, cd->shift);
90-
}
91-
92-
/*
93-
* Initialize the clock data - calculate the appropriate multiplier
94-
* and shift. Also setup a timer to ensure that the epoch is refreshed
95-
* at the appropriate time interval, which will call your update
96-
* handler.
97-
*/
98-
void init_sched_clock(struct clock_data *, void (*)(void),
99-
unsigned int, unsigned long);
100-
101-
/*
102-
* Use this initialization function rather than init_sched_clock() if
103-
* you're using cyc_to_fixed_sched_clock, which will warn if your
104-
* constants are incorrect.
105-
*/
106-
static inline void init_fixed_sched_clock(struct clock_data *cd,
107-
void (*update)(void), unsigned int bits, unsigned long rate,
108-
u32 mult, u32 shift)
109-
{
110-
init_sched_clock(cd, update, bits, rate);
111-
if (cd->mult != mult || cd->shift != shift) {
112-
pr_crit("sched_clock: wrong multiply/shift: %u>>%u vs calculated %u>>%u\n"
113-
"sched_clock: fix multiply/shift to avoid scheduler hiccups\n",
114-
mult, shift, cd->mult, cd->shift);
115-
}
116-
}
117-
11811
extern void sched_clock_postinit(void);
12+
extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate);
11913

12014
#endif

arch/arm/kernel/sched_clock.c

Lines changed: 105 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,61 +14,153 @@
1414

1515
#include <asm/sched_clock.h>
1616

17+
struct clock_data {
18+
u64 epoch_ns;
19+
u32 epoch_cyc;
20+
u32 epoch_cyc_copy;
21+
u32 mult;
22+
u32 shift;
23+
};
24+
1725
static void sched_clock_poll(unsigned long wrap_ticks);
1826
static DEFINE_TIMER(sched_clock_timer, sched_clock_poll, 0, 0);
19-
static void (*sched_clock_update_fn)(void);
27+
28+
static struct clock_data cd = {
29+
.mult = NSEC_PER_SEC / HZ,
30+
};
31+
32+
static u32 __read_mostly sched_clock_mask = 0xffffffff;
33+
34+
static u32 notrace jiffy_sched_clock_read(void)
35+
{
36+
return (u32)(jiffies - INITIAL_JIFFIES);
37+
}
38+
39+
static u32 __read_mostly (*read_sched_clock)(void) = jiffy_sched_clock_read;
40+
41+
static inline u64 cyc_to_ns(u64 cyc, u32 mult, u32 shift)
42+
{
43+
return (cyc * mult) >> shift;
44+
}
45+
46+
static unsigned long long cyc_to_sched_clock(u32 cyc, u32 mask)
47+
{
48+
u64 epoch_ns;
49+
u32 epoch_cyc;
50+
51+
/*
52+
* Load the epoch_cyc and epoch_ns atomically. We do this by
53+
* ensuring that we always write epoch_cyc, epoch_ns and
54+
* epoch_cyc_copy in strict order, and read them in strict order.
55+
* If epoch_cyc and epoch_cyc_copy are not equal, then we're in
56+
* the middle of an update, and we should repeat the load.
57+
*/
58+
do {
59+
epoch_cyc = cd.epoch_cyc;
60+
smp_rmb();
61+
epoch_ns = cd.epoch_ns;
62+
smp_rmb();
63+
} while (epoch_cyc != cd.epoch_cyc_copy);
64+
65+
return epoch_ns + cyc_to_ns((cyc - epoch_cyc) & mask, cd.mult, cd.shift);
66+
}
67+
68+
/*
69+
* Atomically update the sched_clock epoch.
70+
*/
71+
static void notrace update_sched_clock(void)
72+
{
73+
unsigned long flags;
74+
u32 cyc;
75+
u64 ns;
76+
77+
cyc = read_sched_clock();
78+
ns = cd.epoch_ns +
79+
cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask,
80+
cd.mult, cd.shift);
81+
/*
82+
* Write epoch_cyc and epoch_ns in a way that the update is
83+
* detectable in cyc_to_fixed_sched_clock().
84+
*/
85+
raw_local_irq_save(flags);
86+
cd.epoch_cyc = cyc;
87+
smp_wmb();
88+
cd.epoch_ns = ns;
89+
smp_wmb();
90+
cd.epoch_cyc_copy = cyc;
91+
raw_local_irq_restore(flags);
92+
}
2093

2194
static void sched_clock_poll(unsigned long wrap_ticks)
2295
{
2396
mod_timer(&sched_clock_timer, round_jiffies(jiffies + wrap_ticks));
24-
sched_clock_update_fn();
97+
update_sched_clock();
2598
}
2699

27-
void __init init_sched_clock(struct clock_data *cd, void (*update)(void),
28-
unsigned int clock_bits, unsigned long rate)
100+
void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate)
29101
{
30102
unsigned long r, w;
31103
u64 res, wrap;
32104
char r_unit;
33105

34-
sched_clock_update_fn = update;
106+
BUG_ON(bits > 32);
107+
WARN_ON(!irqs_disabled());
108+
WARN_ON(read_sched_clock != jiffy_sched_clock_read);
109+
read_sched_clock = read;
110+
sched_clock_mask = (1 << bits) - 1;
35111

36112
/* calculate the mult/shift to convert counter ticks to ns. */
37-
clocks_calc_mult_shift(&cd->mult, &cd->shift, rate, NSEC_PER_SEC, 0);
113+
clocks_calc_mult_shift(&cd.mult, &cd.shift, rate, NSEC_PER_SEC, 0);
38114

39115
r = rate;
40116
if (r >= 4000000) {
41117
r /= 1000000;
42118
r_unit = 'M';
43-
} else {
119+
} else if (r >= 1000) {
44120
r /= 1000;
45121
r_unit = 'k';
46-
}
122+
} else
123+
r_unit = ' ';
47124

48125
/* calculate how many ns until we wrap */
49-
wrap = cyc_to_ns((1ULL << clock_bits) - 1, cd->mult, cd->shift);
126+
wrap = cyc_to_ns((1ULL << bits) - 1, cd.mult, cd.shift);
50127
do_div(wrap, NSEC_PER_MSEC);
51128
w = wrap;
52129

53130
/* calculate the ns resolution of this counter */
54-
res = cyc_to_ns(1ULL, cd->mult, cd->shift);
131+
res = cyc_to_ns(1ULL, cd.mult, cd.shift);
55132
pr_info("sched_clock: %u bits at %lu%cHz, resolution %lluns, wraps every %lums\n",
56-
clock_bits, r, r_unit, res, w);
133+
bits, r, r_unit, res, w);
57134

58135
/*
59136
* Start the timer to keep sched_clock() properly updated and
60137
* sets the initial epoch.
61138
*/
62139
sched_clock_timer.data = msecs_to_jiffies(w - (w / 10));
63-
update();
140+
update_sched_clock();
64141

65142
/*
66143
* Ensure that sched_clock() starts off at 0ns
67144
*/
68-
cd->epoch_ns = 0;
145+
cd.epoch_ns = 0;
146+
147+
pr_debug("Registered %pF as sched_clock source\n", read);
148+
}
149+
150+
unsigned long long notrace sched_clock(void)
151+
{
152+
u32 cyc = read_sched_clock();
153+
return cyc_to_sched_clock(cyc, sched_clock_mask);
69154
}
70155

71156
void __init sched_clock_postinit(void)
72157
{
158+
/*
159+
* If no sched_clock function has been provided at that point,
160+
* make it the final one one.
161+
*/
162+
if (read_sched_clock == jiffy_sched_clock_read)
163+
setup_sched_clock(jiffy_sched_clock_read, 32, HZ);
164+
73165
sched_clock_poll(sched_clock_timer.data);
74166
}

arch/arm/mach-ixp4xx/common.c

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#include <linux/mm.h>
1818
#include <linux/init.h>
1919
#include <linux/serial.h>
20-
#include <linux/sched.h>
2120
#include <linux/tty.h>
2221
#include <linux/platform_device.h>
2322
#include <linux/serial_core.h>
@@ -403,18 +402,9 @@ void __init ixp4xx_sys_init(void)
403402
/*
404403
* sched_clock()
405404
*/
406-
static DEFINE_CLOCK_DATA(cd);
407-
408-
unsigned long long notrace sched_clock(void)
405+
static u32 notrace ixp4xx_read_sched_clock(void)
409406
{
410-
u32 cyc = *IXP4XX_OSTS;
411-
return cyc_to_sched_clock(&cd, cyc, (u32)~0);
412-
}
413-
414-
static void notrace ixp4xx_update_sched_clock(void)
415-
{
416-
u32 cyc = *IXP4XX_OSTS;
417-
update_sched_clock(&cd, cyc, (u32)~0);
407+
return *IXP4XX_OSTS;
418408
}
419409

420410
/*
@@ -430,7 +420,7 @@ unsigned long ixp4xx_timer_freq = IXP4XX_TIMER_FREQ;
430420
EXPORT_SYMBOL(ixp4xx_timer_freq);
431421
static void __init ixp4xx_clocksource_init(void)
432422
{
433-
init_sched_clock(&cd, ixp4xx_update_sched_clock, 32, ixp4xx_timer_freq);
423+
setup_sched_clock(ixp4xx_read_sched_clock, 32, ixp4xx_timer_freq);
434424

435425
clocksource_mmio_init(NULL, "OSTS", ixp4xx_timer_freq, 200, 32,
436426
ixp4xx_clocksource_read);

arch/arm/mach-mmp/time.c

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
#include <linux/io.h>
2727
#include <linux/irq.h>
28-
#include <linux/sched.h>
2928

3029
#include <asm/sched_clock.h>
3130
#include <mach/addr-map.h>
@@ -42,8 +41,6 @@
4241
#define MAX_DELTA (0xfffffffe)
4342
#define MIN_DELTA (16)
4443

45-
static DEFINE_CLOCK_DATA(cd);
46-
4744
/*
4845
* FIXME: the timer needs some delay to stablize the counter capture
4946
*/
@@ -59,16 +56,9 @@ static inline uint32_t timer_read(void)
5956
return __raw_readl(TIMERS_VIRT_BASE + TMR_CVWR(1));
6057
}
6158

62-
unsigned long long notrace sched_clock(void)
59+
static u32 notrace mmp_read_sched_clock(void)
6360
{
64-
u32 cyc = timer_read();
65-
return cyc_to_sched_clock(&cd, cyc, (u32)~0);
66-
}
67-
68-
static void notrace mmp_update_sched_clock(void)
69-
{
70-
u32 cyc = timer_read();
71-
update_sched_clock(&cd, cyc, (u32)~0);
61+
return timer_read();
7262
}
7363

7464
static irqreturn_t timer_interrupt(int irq, void *dev_id)
@@ -201,7 +191,7 @@ void __init timer_init(int irq)
201191
{
202192
timer_config();
203193

204-
init_sched_clock(&cd, mmp_update_sched_clock, 32, CLOCK_TICK_RATE);
194+
setup_sched_clock(mmp_read_sched_clock, 32, CLOCK_TICK_RATE);
205195

206196
ckevt.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, ckevt.shift);
207197
ckevt.max_delta_ns = clockevent_delta2ns(MAX_DELTA, &ckevt);

0 commit comments

Comments
 (0)