ia64/xen-unstable

view xen/arch/ia64/xen/xentime.c @ 7337:d61142fc7480

Fix reprogram_ac_timer corner case bug, by Tristan Gingold
author djm@kirby.fc.hp.com
date Fri Oct 14 14:11:12 2005 -0600 (2005-10-14)
parents 760f5e85c706
children ebc92fd2fac8
line source
1 /*
2 * xen/arch/ia64/time.c
3 *
4 * Copyright (C) 2005 Hewlett-Packard Co
5 * Dan Magenheimer <dan.magenheimer@hp.com>
6 */
8 #include <linux/config.h>
10 #include <linux/cpu.h>
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/profile.h>
15 #include <linux/sched.h>
16 #include <linux/time.h>
17 #include <linux/interrupt.h>
18 #include <linux/efi.h>
19 #include <linux/profile.h>
20 #include <linux/timex.h>
22 #include <asm/machvec.h>
23 #include <asm/delay.h>
24 #include <asm/hw_irq.h>
25 #include <asm/ptrace.h>
26 #include <asm/sal.h>
27 #include <asm/sections.h>
28 #include <asm/system.h>
29 #include <asm/vcpu.h>
30 #include <linux/jiffies.h> // not included by xen/sched.h
31 #include <xen/softirq.h>
33 seqlock_t xtime_lock __cacheline_aligned_in_smp = SEQLOCK_UNLOCKED;
35 #define TIME_KEEPER_ID 0
36 unsigned long domain0_ready = 0;
37 static s_time_t stime_irq = 0x0; /* System time at last 'time update' */
38 unsigned long itc_scale, ns_scale;
39 unsigned long itc_at_irq;
41 static inline u64 get_time_delta(void)
42 {
43 s64 delta_itc;
44 u64 cur_itc;
46 cur_itc = ia64_get_itc();
48 delta_itc = (s64)(cur_itc - itc_at_irq);
50 /* Ensure that the returned system time is monotonically increasing. */
51 if ( unlikely(delta_itc < 0) ) delta_itc = 0;
52 return cycle_to_ns(delta_itc);
53 }
55 /* We don't expect an absolute cycle value here, since then no way
56 * to prevent overflow for large norminator. Normally this conversion
57 * is used for relative offset.
58 */
59 u64 cycle_to_ns(u64 cycle)
60 {
61 return (cycle * itc_scale) >> 32;
62 }
64 u64 ns_to_cycle(u64 ns)
65 {
66 return (ns * ns_scale) >> 32;
67 }
69 s_time_t get_s_time(void)
70 {
71 s_time_t now;
72 unsigned long flags, seq;
74 do {
75 seq = read_seqbegin(&xtime_lock);
76 now = stime_irq + get_time_delta();
77 } while (unlikely(read_seqretry(&xtime_lock, seq)));
79 return now;
80 }
82 void update_dom_time(struct vcpu *v)
83 {
84 /* N-op here, and let dom0 to manage system time directly */
85 return;
86 }
88 /* Set clock to <secs,usecs> after 00:00:00 UTC, 1 January, 1970. */
89 void do_settime(unsigned long secs, unsigned long nsecs, u64 system_time_base)
90 {
91 /* If absolute system time is managed by dom0, there's no need for such
92 * action since only virtual itc/itm service is provided.
93 */
94 return;
95 }
97 irqreturn_t
98 xen_timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
99 {
100 unsigned long new_itm, old_itc;
102 #if 0
103 #define HEARTBEAT_FREQ 16 // period in seconds
104 #ifdef HEARTBEAT_FREQ
105 static long count = 0;
106 if (!(++count & ((HEARTBEAT_FREQ*1024)-1))) {
107 printf("Heartbeat... iip=%p\n", /*",psr.i=%d,pend=%d\n", */
108 regs->cr_iip /*,
109 VCPU(current,interrupt_delivery_enabled),
110 VCPU(current,pending_interruption) */);
111 count = 0;
112 }
113 #endif
114 #endif
115 if (current->domain == dom0) {
116 // FIXME: there's gotta be a better way of doing this...
117 // We have to ensure that domain0 is launched before we
118 // call vcpu_timer_expired on it
119 //domain0_ready = 1; // moved to xensetup.c
120 VCPU(current,pending_interruption) = 1;
121 }
122 if (domain0_ready && current->domain != dom0) {
123 if(vcpu_timer_expired(dom0->vcpu[0])) {
124 vcpu_pend_timer(dom0->vcpu[0]);
125 //vcpu_set_next_timer(dom0->vcpu[0]);
126 vcpu_wake(dom0->vcpu[0]);
127 }
128 }
129 if (!is_idle_task(current->domain)) {
130 if (vcpu_timer_expired(current)) {
131 vcpu_pend_timer(current);
132 // ensure another timer interrupt happens even if domain doesn't
133 vcpu_set_next_timer(current);
134 vcpu_wake(current);
135 }
136 }
137 new_itm = local_cpu_data->itm_next;
139 if (!VMX_DOMAIN(current) && !time_after(ia64_get_itc(), new_itm))
140 return;
142 if (VMX_DOMAIN(current))
143 vcpu_wake(current);
145 while (1) {
146 new_itm += local_cpu_data->itm_delta;
148 if (smp_processor_id() == TIME_KEEPER_ID) {
149 /*
150 * Here we are in the timer irq handler. We have irqs locally
151 * disabled, but we don't know if the timer_bh is running on
152 * another CPU. We need to avoid to SMP race by acquiring the
153 * xtime_lock.
154 */
155 #ifdef TURN_ME_OFF_FOR_NOW_IA64_XEN
156 write_seqlock(&xtime_lock);
157 #endif
158 #ifdef TURN_ME_OFF_FOR_NOW_IA64_XEN
159 do_timer(regs);
160 #endif
161 local_cpu_data->itm_next = new_itm;
163 /* Updates system time (nanoseconds since boot). */
164 old_itc = itc_at_irq;
165 itc_at_irq = ia64_get_itc();
166 stime_irq += cycle_to_ns(itc_at_irq - old_itc);
168 #ifdef TURN_ME_OFF_FOR_NOW_IA64_XEN
169 write_sequnlock(&xtime_lock);
170 #endif
171 } else
172 local_cpu_data->itm_next = new_itm;
174 if (time_after(new_itm, ia64_get_itc()))
175 break;
176 }
178 do {
179 /*
180 * If we're too close to the next clock tick for
181 * comfort, we increase the safety margin by
182 * intentionally dropping the next tick(s). We do NOT
183 * update itm.next because that would force us to call
184 * do_timer() which in turn would let our clock run
185 * too fast (with the potentially devastating effect
186 * of losing monotony of time).
187 */
188 while (!time_after(new_itm, ia64_get_itc() + local_cpu_data->itm_delta/2))
189 new_itm += local_cpu_data->itm_delta;
190 //#ifdef XEN
191 // vcpu_set_next_timer(current);
192 //#else
193 //printf("***** timer_interrupt: Setting itm to %lx\n",new_itm);
194 ia64_set_itm(new_itm);
195 //#endif
196 /* double check, in case we got hit by a (slow) PMI: */
197 } while (time_after_eq(ia64_get_itc(), new_itm));
198 raise_softirq(AC_TIMER_SOFTIRQ);
200 return IRQ_HANDLED;
201 }
203 static struct irqaction xen_timer_irqaction = {
204 .handler = xen_timer_interrupt,
205 .name = "timer"
206 };
208 void __init
209 ia64_time_init (void)
210 {
211 register_percpu_irq(IA64_TIMER_VECTOR, &xen_timer_irqaction);
212 ia64_init_itm();
213 }
216 /* Late init function (after all CPUs are booted). */
217 int __init init_xen_time()
218 {
219 struct timespec tm;
221 ia64_time_init();
222 itc_scale = 1000000000UL << 32 ;
223 itc_scale /= local_cpu_data->itc_freq;
224 ns_scale = (local_cpu_data->itc_freq << 32) / 1000000000UL;
226 /* System time ticks from zero. */
227 stime_irq = (s_time_t)0;
228 itc_at_irq = ia64_get_itc();
230 printk("Time init:\n");
231 printk(".... System Time: %ldns\n", NOW());
232 printk(".... scale: %16lX\n", itc_scale);
234 return 0;
235 }
237 int reprogram_ac_timer(s_time_t timeout)
238 {
239 struct vcpu *v = current;
240 s_time_t expire;
241 unsigned long seq, cur_itc, itm_next;
243 if (!domain0_ready || timeout == 0) return 1;
245 do {
246 seq = read_seqbegin(&xtime_lock);
247 if ((expire = timeout - NOW()) < 0)
248 return 0;
250 cur_itc = ia64_get_itc();
251 itm_next = cur_itc + ns_to_cycle(expire);
252 } while (unlikely(read_seqretry(&xtime_lock, seq)));
254 local_cpu_data->itm_next = itm_next;
255 vcpu_set_next_timer(current);
256 return 1;
257 }