1 | /****************************************************************************** |
---|
2 | * flushtlb.c |
---|
3 | * |
---|
4 | * TLB flushes are timestamped using a global virtual 'clock' which ticks |
---|
5 | * on any TLB flush on any processor. |
---|
6 | * |
---|
7 | * Copyright (c) 2003-2006, K A Fraser |
---|
8 | */ |
---|
9 | |
---|
10 | #include <xen/config.h> |
---|
11 | #include <xen/sched.h> |
---|
12 | #include <xen/softirq.h> |
---|
13 | #include <asm/flushtlb.h> |
---|
14 | #include <asm/page.h> |
---|
15 | |
---|
16 | /* Debug builds: Wrap frequently to stress-test the wrap logic. */ |
---|
17 | #ifdef NDEBUG |
---|
18 | #define WRAP_MASK (0xFFFFFFFFU) |
---|
19 | #else |
---|
20 | #define WRAP_MASK (0x000003FFU) |
---|
21 | #endif |
---|
22 | |
---|
23 | u32 tlbflush_clock = 1U; |
---|
24 | DEFINE_PER_CPU(u32, tlbflush_time); |
---|
25 | |
---|
26 | /* |
---|
27 | * pre_flush(): Increment the virtual TLB-flush clock. Returns new clock value. |
---|
28 | * |
---|
29 | * This must happen *before* we flush the TLB. If we do it after, we race other |
---|
30 | * CPUs invalidating PTEs. For example, a page invalidated after the flush |
---|
31 | * might get the old timestamp, but this CPU can speculatively fetch the |
---|
32 | * mapping into its TLB after the flush but before inc'ing the clock. |
---|
33 | */ |
---|
34 | static u32 pre_flush(void) |
---|
35 | { |
---|
36 | u32 t, t1, t2; |
---|
37 | |
---|
38 | t = tlbflush_clock; |
---|
39 | do { |
---|
40 | t1 = t2 = t; |
---|
41 | /* Clock wrapped: someone else is leading a global TLB shootdown. */ |
---|
42 | if ( unlikely(t1 == 0) ) |
---|
43 | goto skip_clocktick; |
---|
44 | t2 = (t + 1) & WRAP_MASK; |
---|
45 | } |
---|
46 | while ( unlikely((t = cmpxchg(&tlbflush_clock, t1, t2)) != t1) ); |
---|
47 | |
---|
48 | /* Clock wrapped: we will lead a global TLB shootdown. */ |
---|
49 | if ( unlikely(t2 == 0) ) |
---|
50 | raise_softirq(NEW_TLBFLUSH_CLOCK_PERIOD_SOFTIRQ); |
---|
51 | |
---|
52 | skip_clocktick: |
---|
53 | return t2; |
---|
54 | } |
---|
55 | |
---|
56 | /* |
---|
57 | * post_flush(): Update this CPU's timestamp with specified clock value. |
---|
58 | * |
---|
59 | * Note that this happens *after* flushing the TLB, as otherwise we can race a |
---|
60 | * NEED_FLUSH() test on another CPU. (e.g., other CPU sees the updated CPU |
---|
61 | * stamp and so does not force a synchronous TLB flush, but the flush in this |
---|
62 | * function hasn't yet occurred and so the TLB might be stale). The ordering |
---|
63 | * would only actually matter if this function were interruptible, and |
---|
64 | * something that abuses the stale mapping could exist in an interrupt |
---|
65 | * handler. In fact neither of these is the case, so really we are being ultra |
---|
66 | * paranoid. |
---|
67 | */ |
---|
68 | static void post_flush(u32 t) |
---|
69 | { |
---|
70 | this_cpu(tlbflush_time) = t; |
---|
71 | } |
---|
72 | |
---|
73 | void write_cr3(unsigned long cr3) |
---|
74 | { |
---|
75 | unsigned long flags; |
---|
76 | u32 t; |
---|
77 | |
---|
78 | /* This non-reentrant function is sometimes called in interrupt context. */ |
---|
79 | local_irq_save(flags); |
---|
80 | |
---|
81 | t = pre_flush(); |
---|
82 | |
---|
83 | #ifdef USER_MAPPINGS_ARE_GLOBAL |
---|
84 | __pge_off(); |
---|
85 | __asm__ __volatile__ ( "mov %0, %%cr3" : : "r" (cr3) : "memory" ); |
---|
86 | __pge_on(); |
---|
87 | #else |
---|
88 | __asm__ __volatile__ ( "mov %0, %%cr3" : : "r" (cr3) : "memory" ); |
---|
89 | #endif |
---|
90 | |
---|
91 | post_flush(t); |
---|
92 | |
---|
93 | local_irq_restore(flags); |
---|
94 | } |
---|
95 | |
---|
96 | void local_flush_tlb(void) |
---|
97 | { |
---|
98 | unsigned long flags; |
---|
99 | u32 t; |
---|
100 | |
---|
101 | /* This non-reentrant function is sometimes called in interrupt context. */ |
---|
102 | local_irq_save(flags); |
---|
103 | |
---|
104 | t = pre_flush(); |
---|
105 | |
---|
106 | #ifdef USER_MAPPINGS_ARE_GLOBAL |
---|
107 | __pge_off(); |
---|
108 | __pge_on(); |
---|
109 | #else |
---|
110 | __asm__ __volatile__ ( "mov %0, %%cr3" : : "r" (read_cr3()) : "memory" ); |
---|
111 | #endif |
---|
112 | |
---|
113 | post_flush(t); |
---|
114 | |
---|
115 | local_irq_restore(flags); |
---|
116 | } |
---|