ia64/xen-unstable

view xen/arch/x86/hvm/vpt.c @ 19825:81edfffb3aff

Scaling guest's TSC when the target machine's frequency is different
with its requirement.

Using trap&emulate for guest's each rdtsc instruction first, maybe it
can be optimized later.

Signed-off-by: Xiantao Zhang <xiantao.zhang@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Jun 24 11:05:22 2009 +0100 (2009-06-24)
parents 82bbce59b65d
children
line source
1 /*
2 * vpt.c: Virtual Platform Timer
3 *
4 * Copyright (c) 2006, Xiaowei Yang, Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place - Suite 330, Boston, MA 02111-1307 USA.
18 */
20 #include <xen/time.h>
21 #include <asm/hvm/support.h>
22 #include <asm/hvm/vpt.h>
23 #include <asm/event.h>
25 #define mode_is(d, name) \
26 ((d)->arch.hvm_domain.params[HVM_PARAM_TIMER_MODE] == HVMPTM_##name)
28 void hvm_init_guest_time(struct domain *d)
29 {
30 struct pl_time *pl = &d->arch.hvm_domain.pl_time;
32 spin_lock_init(&pl->pl_time_lock);
33 pl->stime_offset = -(u64)get_s_time();
34 pl->last_guest_time = 0;
36 d->arch.hvm_domain.gtsc_khz = opt_softtsc ? 1000000 : cpu_khz;
37 d->arch.hvm_domain.tsc_scaled = 0;
38 }
40 u64 hvm_get_guest_time(struct vcpu *v)
41 {
42 struct pl_time *pl = &v->domain->arch.hvm_domain.pl_time;
43 u64 now;
45 /* Called from device models shared with PV guests. Be careful. */
46 ASSERT(is_hvm_vcpu(v));
48 spin_lock(&pl->pl_time_lock);
49 now = get_s_time() + pl->stime_offset;
50 if ( (int64_t)(now - pl->last_guest_time) >= 0 )
51 pl->last_guest_time = now;
52 else
53 now = pl->last_guest_time;
54 spin_unlock(&pl->pl_time_lock);
56 return now + v->arch.hvm_vcpu.stime_offset;
57 }
59 void hvm_set_guest_time(struct vcpu *v, u64 guest_time)
60 {
61 v->arch.hvm_vcpu.stime_offset += guest_time - hvm_get_guest_time(v);
62 }
64 static int pt_irq_vector(struct periodic_time *pt, enum hvm_intsrc src)
65 {
66 struct vcpu *v = pt->vcpu;
67 unsigned int gsi, isa_irq;
69 if ( pt->source == PTSRC_lapic )
70 return pt->irq;
72 isa_irq = pt->irq;
73 gsi = hvm_isa_irq_to_gsi(isa_irq);
75 if ( src == hvm_intsrc_pic )
76 return (v->domain->arch.hvm_domain.vpic[isa_irq >> 3].irq_base
77 + (isa_irq & 7));
79 ASSERT(src == hvm_intsrc_lapic);
80 return domain_vioapic(v->domain)->redirtbl[gsi].fields.vector;
81 }
83 static int pt_irq_masked(struct periodic_time *pt)
84 {
85 struct vcpu *v = pt->vcpu;
86 unsigned int gsi, isa_irq;
87 uint8_t pic_imr;
89 if ( pt->source == PTSRC_lapic )
90 {
91 struct vlapic *vlapic = vcpu_vlapic(v);
92 return (!vlapic_enabled(vlapic) ||
93 (vlapic_get_reg(vlapic, APIC_LVTT) & APIC_LVT_MASKED));
94 }
96 isa_irq = pt->irq;
97 gsi = hvm_isa_irq_to_gsi(isa_irq);
98 pic_imr = v->domain->arch.hvm_domain.vpic[isa_irq >> 3].imr;
100 return (((pic_imr & (1 << (isa_irq & 7))) || !vlapic_accept_pic_intr(v)) &&
101 domain_vioapic(v->domain)->redirtbl[gsi].fields.mask);
102 }
104 static void pt_lock(struct periodic_time *pt)
105 {
106 struct vcpu *v;
108 for ( ; ; )
109 {
110 v = pt->vcpu;
111 spin_lock(&v->arch.hvm_vcpu.tm_lock);
112 if ( likely(pt->vcpu == v) )
113 break;
114 spin_unlock(&v->arch.hvm_vcpu.tm_lock);
115 }
116 }
118 static void pt_unlock(struct periodic_time *pt)
119 {
120 spin_unlock(&pt->vcpu->arch.hvm_vcpu.tm_lock);
121 }
123 static void pt_process_missed_ticks(struct periodic_time *pt)
124 {
125 s_time_t missed_ticks, now = NOW();
127 if ( pt->one_shot )
128 return;
130 missed_ticks = now - pt->scheduled;
131 if ( missed_ticks <= 0 )
132 return;
134 missed_ticks = missed_ticks / (s_time_t) pt->period + 1;
135 if ( mode_is(pt->vcpu->domain, no_missed_ticks_pending) )
136 pt->do_not_freeze = !pt->pending_intr_nr;
137 else
138 pt->pending_intr_nr += missed_ticks;
139 pt->scheduled += missed_ticks * pt->period;
140 }
142 static void pt_freeze_time(struct vcpu *v)
143 {
144 if ( !mode_is(v->domain, delay_for_missed_ticks) )
145 return;
147 v->arch.hvm_vcpu.guest_time = hvm_get_guest_time(v);
148 }
150 static void pt_thaw_time(struct vcpu *v)
151 {
152 if ( !mode_is(v->domain, delay_for_missed_ticks) )
153 return;
155 if ( v->arch.hvm_vcpu.guest_time == 0 )
156 return;
158 hvm_set_guest_time(v, v->arch.hvm_vcpu.guest_time);
159 v->arch.hvm_vcpu.guest_time = 0;
160 }
162 void pt_save_timer(struct vcpu *v)
163 {
164 struct list_head *head = &v->arch.hvm_vcpu.tm_list;
165 struct periodic_time *pt;
167 if ( test_bit(_VPF_blocked, &v->pause_flags) )
168 return;
170 spin_lock(&v->arch.hvm_vcpu.tm_lock);
172 list_for_each_entry ( pt, head, list )
173 if ( !pt->do_not_freeze )
174 stop_timer(&pt->timer);
176 pt_freeze_time(v);
178 spin_unlock(&v->arch.hvm_vcpu.tm_lock);
179 }
181 void pt_restore_timer(struct vcpu *v)
182 {
183 struct list_head *head = &v->arch.hvm_vcpu.tm_list;
184 struct periodic_time *pt;
186 spin_lock(&v->arch.hvm_vcpu.tm_lock);
188 list_for_each_entry ( pt, head, list )
189 {
190 pt_process_missed_ticks(pt);
191 set_timer(&pt->timer, pt->scheduled);
192 }
194 pt_thaw_time(v);
196 spin_unlock(&v->arch.hvm_vcpu.tm_lock);
197 }
199 static void pt_timer_fn(void *data)
200 {
201 struct periodic_time *pt = data;
203 pt_lock(pt);
205 pt->pending_intr_nr++;
206 pt->do_not_freeze = 0;
208 if ( !pt->one_shot )
209 {
210 pt->scheduled += pt->period;
211 pt_process_missed_ticks(pt);
212 set_timer(&pt->timer, pt->scheduled);
213 }
215 if ( !pt_irq_masked(pt) )
216 vcpu_kick(pt->vcpu);
218 pt_unlock(pt);
219 }
221 void pt_update_irq(struct vcpu *v)
222 {
223 struct list_head *head = &v->arch.hvm_vcpu.tm_list;
224 struct periodic_time *pt, *earliest_pt = NULL;
225 uint64_t max_lag = -1ULL;
226 int irq, is_lapic;
228 spin_lock(&v->arch.hvm_vcpu.tm_lock);
230 list_for_each_entry ( pt, head, list )
231 {
232 if ( !pt_irq_masked(pt) && pt->pending_intr_nr &&
233 ((pt->last_plt_gtime + pt->period_cycles) < max_lag) )
234 {
235 max_lag = pt->last_plt_gtime + pt->period_cycles;
236 earliest_pt = pt;
237 }
238 }
240 if ( earliest_pt == NULL )
241 {
242 spin_unlock(&v->arch.hvm_vcpu.tm_lock);
243 return;
244 }
246 earliest_pt->irq_issued = 1;
247 irq = earliest_pt->irq;
248 is_lapic = (earliest_pt->source == PTSRC_lapic);
250 spin_unlock(&v->arch.hvm_vcpu.tm_lock);
252 if ( is_lapic )
253 {
254 vlapic_set_irq(vcpu_vlapic(v), irq, 0);
255 }
256 else
257 {
258 hvm_isa_irq_deassert(v->domain, irq);
259 hvm_isa_irq_assert(v->domain, irq);
260 }
261 }
263 static struct periodic_time *is_pt_irq(
264 struct vcpu *v, struct hvm_intack intack)
265 {
266 struct list_head *head = &v->arch.hvm_vcpu.tm_list;
267 struct periodic_time *pt;
269 list_for_each_entry ( pt, head, list )
270 {
271 if ( pt->pending_intr_nr && pt->irq_issued &&
272 (intack.vector == pt_irq_vector(pt, intack.source)) )
273 return pt;
274 }
276 return NULL;
277 }
279 void pt_intr_post(struct vcpu *v, struct hvm_intack intack)
280 {
281 struct periodic_time *pt;
282 time_cb *cb;
283 void *cb_priv;
285 spin_lock(&v->arch.hvm_vcpu.tm_lock);
287 pt = is_pt_irq(v, intack);
288 if ( pt == NULL )
289 {
290 spin_unlock(&v->arch.hvm_vcpu.tm_lock);
291 return;
292 }
294 pt->irq_issued = 0;
296 if ( pt->one_shot )
297 {
298 if ( pt->on_list )
299 list_del(&pt->list);
300 pt->on_list = 0;
301 }
302 else
303 {
304 if ( mode_is(v->domain, one_missed_tick_pending) ||
305 mode_is(v->domain, no_missed_ticks_pending) )
306 {
307 pt->last_plt_gtime = hvm_get_guest_time(v);
308 pt->pending_intr_nr = 0; /* 'collapse' all missed ticks */
309 }
310 else
311 {
312 pt->last_plt_gtime += pt->period_cycles;
313 pt->pending_intr_nr--;
314 }
315 }
317 if ( mode_is(v->domain, delay_for_missed_ticks) &&
318 (hvm_get_guest_time(v) < pt->last_plt_gtime) )
319 hvm_set_guest_time(v, pt->last_plt_gtime);
321 cb = pt->cb;
322 cb_priv = pt->priv;
324 spin_unlock(&v->arch.hvm_vcpu.tm_lock);
326 if ( cb != NULL )
327 cb(v, cb_priv);
328 }
330 void pt_reset(struct vcpu *v)
331 {
332 struct list_head *head = &v->arch.hvm_vcpu.tm_list;
333 struct periodic_time *pt;
335 spin_lock(&v->arch.hvm_vcpu.tm_lock);
337 list_for_each_entry ( pt, head, list )
338 {
339 pt->pending_intr_nr = 0;
340 pt->last_plt_gtime = hvm_get_guest_time(pt->vcpu);
341 pt->scheduled = NOW() + pt->period;
342 set_timer(&pt->timer, pt->scheduled);
343 }
345 spin_unlock(&v->arch.hvm_vcpu.tm_lock);
346 }
348 void pt_migrate(struct vcpu *v)
349 {
350 struct list_head *head = &v->arch.hvm_vcpu.tm_list;
351 struct periodic_time *pt;
353 spin_lock(&v->arch.hvm_vcpu.tm_lock);
355 list_for_each_entry ( pt, head, list )
356 migrate_timer(&pt->timer, v->processor);
358 spin_unlock(&v->arch.hvm_vcpu.tm_lock);
359 }
361 void create_periodic_time(
362 struct vcpu *v, struct periodic_time *pt, uint64_t delta,
363 uint64_t period, uint8_t irq, time_cb *cb, void *data)
364 {
365 ASSERT(pt->source != 0);
367 destroy_periodic_time(pt);
369 spin_lock(&v->arch.hvm_vcpu.tm_lock);
371 pt->pending_intr_nr = 0;
372 pt->do_not_freeze = 0;
373 pt->irq_issued = 0;
375 /* Periodic timer must be at least 0.1ms. */
376 if ( (period < 100000) && period )
377 {
378 if ( !test_and_set_bool(pt->warned_timeout_too_short) )
379 gdprintk(XENLOG_WARNING, "HVM_PlatformTime: program too "
380 "small period %"PRIu64"\n", period);
381 period = 100000;
382 }
384 pt->period = period;
385 pt->vcpu = v;
386 pt->last_plt_gtime = hvm_get_guest_time(pt->vcpu);
387 pt->irq = irq;
388 pt->period_cycles = (u64)period;
389 pt->one_shot = !period;
390 pt->scheduled = NOW() + delta;
392 if ( !pt->one_shot )
393 {
394 if ( v->domain->arch.hvm_domain.params[HVM_PARAM_VPT_ALIGN] )
395 {
396 pt->scheduled = align_timer(pt->scheduled, pt->period);
397 }
398 else if ( pt->source == PTSRC_lapic )
399 {
400 /*
401 * Offset LAPIC ticks from other timer ticks. Otherwise guests
402 * which use LAPIC ticks for process accounting can see long
403 * sequences of process ticks incorrectly accounted to interrupt
404 * processing (seen with RHEL3 guest).
405 */
406 pt->scheduled += delta >> 1;
407 }
408 }
410 pt->cb = cb;
411 pt->priv = data;
413 pt->on_list = 1;
414 list_add(&pt->list, &v->arch.hvm_vcpu.tm_list);
416 init_timer(&pt->timer, pt_timer_fn, pt, v->processor);
417 set_timer(&pt->timer, pt->scheduled);
419 spin_unlock(&v->arch.hvm_vcpu.tm_lock);
420 }
422 void destroy_periodic_time(struct periodic_time *pt)
423 {
424 /* Was this structure previously initialised by create_periodic_time()? */
425 if ( pt->vcpu == NULL )
426 return;
428 pt_lock(pt);
429 if ( pt->on_list )
430 list_del(&pt->list);
431 pt->on_list = 0;
432 pt_unlock(pt);
434 /*
435 * pt_timer_fn() can run until this kill_timer() returns. We must do this
436 * outside pt_lock() otherwise we can deadlock with pt_timer_fn().
437 */
438 kill_timer(&pt->timer);
439 }