1 | /* |
---|
2 | * Smp timebase synchronization for ppc. |
---|
3 | * |
---|
4 | * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se) |
---|
5 | * |
---|
6 | */ |
---|
7 | /* XXX Xen hacks ... */ |
---|
8 | #define get_tb() get_timebase() |
---|
9 | #define set_tb(u,l) set_timebase(u,l) |
---|
10 | #define kmalloc(s,f) xmalloc_bytes(s); |
---|
11 | #define kfree(p) xfree(p) |
---|
12 | #define abs(x) ({ \ |
---|
13 | int __x = (x); \ |
---|
14 | (__x < 0) ? -__x : __x; \ |
---|
15 | }) |
---|
16 | |
---|
17 | #include <xen/kernel.h> |
---|
18 | #include <xen/sched.h> |
---|
19 | #include <xen/smp.h> |
---|
20 | #ifndef __XEN__ |
---|
21 | #include <linux/unistd.h> |
---|
22 | #endif |
---|
23 | #include <xen/init.h> |
---|
24 | #include <asm/atomic.h> |
---|
25 | #include <asm/smp.h> |
---|
26 | #include <asm/time.h> |
---|
27 | |
---|
28 | |
---|
29 | /* don't mess with IRQs */ |
---|
30 | #define local_irq_enable() |
---|
31 | #define local_irq_disable() |
---|
32 | |
---|
33 | #define NUM_ITER 300 |
---|
34 | |
---|
35 | #undef DEBUG |
---|
36 | #ifdef DEBUG |
---|
37 | #define DBG(fmt...) printk(fmt) |
---|
38 | #else |
---|
39 | #define DBG(fmt...) |
---|
40 | #endif |
---|
41 | |
---|
42 | enum { |
---|
43 | kExit=0, kSetAndTest, kTest |
---|
44 | }; |
---|
45 | |
---|
46 | static struct { |
---|
47 | volatile u64 tb; |
---|
48 | volatile u64 mark; |
---|
49 | volatile int cmd; |
---|
50 | volatile int handshake; |
---|
51 | int filler[2]; |
---|
52 | |
---|
53 | volatile int ack; |
---|
54 | int filler2[7]; |
---|
55 | |
---|
56 | volatile int race_result; |
---|
57 | } *tbsync; |
---|
58 | |
---|
59 | static volatile int running; |
---|
60 | |
---|
61 | static void __devinit enter_contest(u64 mark, long add) |
---|
62 | { |
---|
63 | while (get_tb() < mark) |
---|
64 | tbsync->race_result = add; |
---|
65 | } |
---|
66 | |
---|
67 | void __devinit smp_generic_take_timebase(void) |
---|
68 | { |
---|
69 | int cmd; |
---|
70 | u64 tb; |
---|
71 | |
---|
72 | local_irq_disable(); |
---|
73 | while (!running) |
---|
74 | barrier(); |
---|
75 | rmb(); |
---|
76 | |
---|
77 | for (;;) { |
---|
78 | tbsync->ack = 1; |
---|
79 | while (!tbsync->handshake) |
---|
80 | barrier(); |
---|
81 | rmb(); |
---|
82 | |
---|
83 | cmd = tbsync->cmd; |
---|
84 | tb = tbsync->tb; |
---|
85 | mb(); |
---|
86 | tbsync->ack = 0; |
---|
87 | if (cmd == kExit) |
---|
88 | break; |
---|
89 | |
---|
90 | while (tbsync->handshake) |
---|
91 | barrier(); |
---|
92 | if (cmd == kSetAndTest) |
---|
93 | set_tb(tb >> 32, tb & 0xfffffffful); |
---|
94 | enter_contest(tbsync->mark, -1); |
---|
95 | } |
---|
96 | local_irq_enable(); |
---|
97 | } |
---|
98 | |
---|
99 | static int __devinit start_contest(int cmd, long offset, int num) |
---|
100 | { |
---|
101 | int i, score=0; |
---|
102 | u64 tb; |
---|
103 | long mark; |
---|
104 | |
---|
105 | tbsync->cmd = cmd; |
---|
106 | |
---|
107 | local_irq_disable(); |
---|
108 | for (i = -3; i < num; ) { |
---|
109 | tb = get_tb() + 400; |
---|
110 | tbsync->tb = tb + offset; |
---|
111 | tbsync->mark = mark = tb + 400; |
---|
112 | |
---|
113 | wmb(); |
---|
114 | |
---|
115 | tbsync->handshake = 1; |
---|
116 | while (tbsync->ack) |
---|
117 | barrier(); |
---|
118 | |
---|
119 | while (get_tb() <= tb) |
---|
120 | barrier(); |
---|
121 | tbsync->handshake = 0; |
---|
122 | enter_contest(mark, 1); |
---|
123 | |
---|
124 | while (!tbsync->ack) |
---|
125 | barrier(); |
---|
126 | |
---|
127 | if (i++ > 0) |
---|
128 | score += tbsync->race_result; |
---|
129 | } |
---|
130 | local_irq_enable(); |
---|
131 | return score; |
---|
132 | } |
---|
133 | |
---|
134 | void __devinit smp_generic_give_timebase(void) |
---|
135 | { |
---|
136 | int i, score, score2, old, min=0, max=5000, offset=1000; |
---|
137 | |
---|
138 | printk("Synchronizing timebase...\n"); |
---|
139 | |
---|
140 | /* if this fails then this kernel won't work anyway... */ |
---|
141 | tbsync = kmalloc( sizeof(*tbsync), GFP_KERNEL ); |
---|
142 | memset( tbsync, 0, sizeof(*tbsync) ); |
---|
143 | mb(); |
---|
144 | running = 1; |
---|
145 | |
---|
146 | while (!tbsync->ack) |
---|
147 | barrier(); |
---|
148 | |
---|
149 | DBG("Got ack\n"); |
---|
150 | |
---|
151 | /* binary search */ |
---|
152 | for (old = -1; old != offset ; offset = (min+max) / 2) { |
---|
153 | score = start_contest(kSetAndTest, offset, NUM_ITER); |
---|
154 | |
---|
155 | DBG("score %d, offset %d\n", score, offset ); |
---|
156 | |
---|
157 | if( score > 0 ) |
---|
158 | max = offset; |
---|
159 | else |
---|
160 | min = offset; |
---|
161 | old = offset; |
---|
162 | } |
---|
163 | score = start_contest(kSetAndTest, min, NUM_ITER); |
---|
164 | score2 = start_contest(kSetAndTest, max, NUM_ITER); |
---|
165 | |
---|
166 | DBG("Min %d (score %d), Max %d (score %d)\n", |
---|
167 | min, score, max, score2); |
---|
168 | score = abs(score); |
---|
169 | score2 = abs(score2); |
---|
170 | offset = (score < score2) ? min : max; |
---|
171 | |
---|
172 | /* guard against inaccurate mttb */ |
---|
173 | for (i = 0; i < 10; i++) { |
---|
174 | start_contest(kSetAndTest, offset, NUM_ITER/10); |
---|
175 | |
---|
176 | if ((score2 = start_contest(kTest, offset, NUM_ITER)) < 0) |
---|
177 | score2 = -score2; |
---|
178 | if (score2 <= score || score2 < 20) |
---|
179 | break; |
---|
180 | } |
---|
181 | DBG("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER ); |
---|
182 | |
---|
183 | /* exiting */ |
---|
184 | tbsync->cmd = kExit; |
---|
185 | wmb(); |
---|
186 | tbsync->handshake = 1; |
---|
187 | while (tbsync->ack) |
---|
188 | barrier(); |
---|
189 | tbsync->handshake = 0; |
---|
190 | kfree(tbsync); |
---|
191 | tbsync = NULL; |
---|
192 | running = 0; |
---|
193 | } |
---|