[34] | 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 | } |
---|