ia64/xen-unstable

annotate xen/arch/ia64/xen/vhpt.c @ 10786:86e5d8458c08

[IA64] live migration

Shadow mode and live migration.

Virtualize Dirty bit.

Signed-off-by: Tristan Gingold <tristan.gingold@bull.net>
author awilliam@xenbuild.aw
date Wed Jul 26 09:36:36 2006 -0600 (2006-07-26)
parents 72caf2612e52
children 7cde0d938ef4
rev   line source
djm@6458 1 /*
djm@6458 2 * Initialize VHPT support.
djm@6458 3 *
djm@6458 4 * Copyright (C) 2004 Hewlett-Packard Co
djm@6458 5 * Dan Magenheimer <dan.magenheimer@hp.com>
djm@6458 6 */
djm@6458 7 #include <linux/config.h>
djm@6458 8 #include <linux/kernel.h>
djm@6458 9 #include <linux/init.h>
djm@6458 10
djm@6458 11 #include <asm/processor.h>
djm@6458 12 #include <asm/system.h>
djm@6458 13 #include <asm/pgalloc.h>
djm@6458 14 #include <asm/page.h>
djm@6458 15 #include <asm/vhpt.h>
awilliam@10013 16 #include <asm/vcpu.h>
awilliam@10013 17
awilliam@10013 18 /* Defined in tlb.c */
awilliam@10013 19 extern void ia64_global_tlb_purge(UINT64 start, UINT64 end, UINT64 nbits);
djm@6458 20
awilliam@9748 21 extern long running_on_sim;
awilliam@9748 22
awilliam@9000 23 DEFINE_PER_CPU (unsigned long, vhpt_paddr);
awilliam@9000 24 DEFINE_PER_CPU (unsigned long, vhpt_pend);
djm@6458 25
awilliam@10567 26 void vhpt_flush(void)
djm@6458 27 {
awilliam@10016 28 struct vhpt_lf_entry *v = __va(__ia64_per_cpu_var(vhpt_paddr));
awilliam@9005 29 int i;
djm@6458 30
awilliam@10013 31 for (i = 0; i < VHPT_NUM_ENTRIES; i++, v++)
awilliam@10013 32 v->ti_tag = INVALID_TI_TAG;
djm@6458 33 }
awilliam@10013 34
awilliam@10013 35 static void vhpt_erase(void)
awilliam@10013 36 {
awilliam@10013 37 struct vhpt_lf_entry *v = (struct vhpt_lf_entry *)VHPT_ADDR;
awilliam@10013 38 int i;
awilliam@10013 39
djm@6458 40 for (i = 0; i < VHPT_NUM_ENTRIES; i++, v++) {
djm@6458 41 v->itir = 0;
djm@6458 42 v->CChain = 0;
djm@6458 43 v->page_flags = 0;
djm@6458 44 v->ti_tag = INVALID_TI_TAG;
djm@6458 45 }
djm@6458 46 // initialize cache too???
djm@6458 47 }
djm@6458 48
djm@6458 49
awilliam@9000 50 static void vhpt_map(unsigned long pte)
djm@6458 51 {
djm@6458 52 unsigned long psr;
djm@6458 53
djm@6458 54 psr = ia64_clear_ic();
awilliam@9000 55 ia64_itr(0x2, IA64_TR_VHPT, VHPT_ADDR, pte, VHPT_SIZE_LOG2);
djm@6458 56 ia64_set_psr(psr);
djm@6458 57 ia64_srlz_i();
djm@6458 58 }
djm@6458 59
awilliam@9748 60 void vhpt_insert (unsigned long vadr, unsigned long pte, unsigned long logps)
awilliam@9748 61 {
awilliam@9748 62 struct vhpt_lf_entry *vlfe = (struct vhpt_lf_entry *)ia64_thash(vadr);
awilliam@9748 63 unsigned long tag = ia64_ttag (vadr);
awilliam@9748 64
awilliam@9748 65 /* No need to first disable the entry, since VHPT is per LP
awilliam@9748 66 and VHPT is TR mapped. */
awilliam@9748 67 vlfe->itir = logps;
awilliam@9748 68 vlfe->page_flags = pte | _PAGE_P;
awilliam@9748 69 vlfe->ti_tag = tag;
awilliam@9748 70 }
awilliam@9748 71
djm@6458 72 void vhpt_multiple_insert(unsigned long vaddr, unsigned long pte, unsigned long logps)
djm@6458 73 {
djm@6458 74 unsigned long mask = (1L << logps) - 1;
djm@6458 75 int i;
djm@6458 76
djm@6458 77 if (logps-PAGE_SHIFT > 10 && !running_on_sim) {
djm@6458 78 // if this happens, we may want to revisit this algorithm
awilliam@9748 79 panic("vhpt_multiple_insert:logps-PAGE_SHIFT>10,spinning..\n");
djm@6458 80 }
djm@6458 81 if (logps-PAGE_SHIFT > 2) {
djm@6458 82 // FIXME: Should add counter here to see how often this
djm@6458 83 // happens (e.g. for 16MB pages!) and determine if it
djm@6458 84 // is a performance problem. On a quick look, it takes
djm@6458 85 // about 39000 instrs for a 16MB page and it seems to occur
djm@6458 86 // only a few times/second, so OK for now.
djm@6458 87 // An alternate solution would be to just insert the one
djm@6458 88 // 16KB in the vhpt (but with the full mapping)?
djm@6458 89 //printf("vhpt_multiple_insert: logps-PAGE_SHIFT==%d,"
djm@6458 90 //"va=%p, pa=%p, pa-masked=%p\n",
djm@6458 91 //logps-PAGE_SHIFT,vaddr,pte&_PFN_MASK,
djm@6458 92 //(pte&_PFN_MASK)&~mask);
djm@6458 93 }
djm@6458 94 vaddr &= ~mask;
djm@6458 95 pte = ((pte & _PFN_MASK) & ~mask) | (pte & ~_PFN_MASK);
djm@6458 96 for (i = 1L << (logps-PAGE_SHIFT); i > 0; i--) {
djm@6458 97 vhpt_insert(vaddr,pte,logps<<2);
djm@6458 98 vaddr += PAGE_SIZE;
djm@6458 99 }
djm@6458 100 }
djm@6458 101
djm@6458 102 void vhpt_init(void)
djm@6458 103 {
awilliam@9000 104 unsigned long paddr, pte;
awilliam@8998 105 struct page_info *page;
djm@6458 106 #if !VHPT_ENABLED
djm@6458 107 return;
djm@6458 108 #endif
djm@6458 109 /* This allocation only holds true if vhpt table is unique for
djm@6458 110 * all domains. Or else later new vhpt table should be allocated
djm@6458 111 * from domain heap when each domain is created. Assume xen buddy
djm@6458 112 * allocator can provide natural aligned page by order?
djm@6458 113 */
awilliam@8998 114 page = alloc_domheap_pages(NULL, VHPT_SIZE_LOG2 - PAGE_SHIFT, 0);
awilliam@9748 115 if (!page)
awilliam@9748 116 panic("vhpt_init: can't allocate VHPT!\n");
awilliam@9000 117 paddr = page_to_maddr(page);
awilliam@10013 118 if (paddr & ((1 << VHPT_SIZE_LOG2) - 1))
awilliam@10013 119 panic("vhpt_init: bad VHPT alignment!\n");
awilliam@9000 120 __get_cpu_var(vhpt_paddr) = paddr;
awilliam@10013 121 __get_cpu_var(vhpt_pend) = paddr + (1 << VHPT_SIZE_LOG2) - 1;
awilliam@9005 122 printf("vhpt_init: vhpt paddr=0x%lx, end=0x%lx\n",
awilliam@9005 123 paddr, __get_cpu_var(vhpt_pend));
awilliam@9000 124 pte = pte_val(pfn_pte(paddr >> PAGE_SHIFT, PAGE_KERNEL));
awilliam@9000 125 vhpt_map(pte);
djm@6458 126 ia64_set_pta(VHPT_ADDR | (1 << 8) | (VHPT_SIZE_LOG2 << 2) |
djm@6458 127 VHPT_ENABLED);
awilliam@10013 128 vhpt_erase();
djm@6458 129 }
djm@6458 130
djm@6864 131
awilliam@10572 132 void vcpu_flush_vtlb_all(struct vcpu *v)
awilliam@10013 133 {
awilliam@10013 134 /* First VCPU tlb. */
awilliam@10013 135 vcpu_purge_tr_entry(&PSCBX(v,dtlb));
awilliam@10013 136 vcpu_purge_tr_entry(&PSCBX(v,itlb));
awilliam@10013 137
awilliam@10013 138 /* Then VHPT. */
awilliam@10013 139 vhpt_flush ();
awilliam@10013 140
awilliam@10013 141 /* Then mTLB. */
awilliam@10013 142 local_flush_tlb_all ();
awilliam@10013 143
awilliam@10013 144 /* We could clear bit in d->domain_dirty_cpumask only if domain d in
awilliam@10013 145 not running on this processor. There is currently no easy way to
awilliam@10013 146 check this. */
awilliam@10013 147 }
awilliam@10013 148
awilliam@10572 149 static void __vcpu_flush_vtlb_all(void *vcpu)
awilliam@10572 150 {
awilliam@10572 151 vcpu_flush_vtlb_all((struct vcpu*)vcpu);
awilliam@10572 152 }
awilliam@10572 153
awilliam@10013 154 void domain_flush_vtlb_all (void)
awilliam@10013 155 {
awilliam@10013 156 int cpu = smp_processor_id ();
awilliam@10013 157 struct vcpu *v;
awilliam@10013 158
awilliam@10430 159 for_each_vcpu (current->domain, v) {
awilliam@10430 160 if (!test_bit(_VCPUF_initialised, &v->vcpu_flags))
awilliam@10430 161 continue;
awilliam@10430 162
awilliam@10013 163 if (v->processor == cpu)
awilliam@10572 164 vcpu_flush_vtlb_all(v);
awilliam@10013 165 else
awilliam@10572 166 smp_call_function_single(v->processor,
awilliam@10572 167 __vcpu_flush_vtlb_all,
awilliam@10572 168 v, 1, 1);
awilliam@10430 169 }
awilliam@10013 170 }
awilliam@10013 171
awilliam@10013 172 static void cpu_flush_vhpt_range (int cpu, u64 vadr, u64 addr_range)
awilliam@10013 173 {
awilliam@10013 174 void *vhpt_base = __va(per_cpu(vhpt_paddr, cpu));
awilliam@10013 175
awilliam@10013 176 while ((long)addr_range > 0) {
awilliam@10013 177 /* Get the VHPT entry. */
awilliam@10013 178 unsigned int off = ia64_thash(vadr) - VHPT_ADDR;
awilliam@10013 179 volatile struct vhpt_lf_entry *v;
awilliam@10013 180 v = vhpt_base + off;
awilliam@10013 181 v->ti_tag = INVALID_TI_TAG;
awilliam@10013 182 addr_range -= PAGE_SIZE;
awilliam@10013 183 vadr += PAGE_SIZE;
awilliam@10013 184 }
awilliam@10013 185 }
awilliam@10013 186
awilliam@10013 187 void vcpu_flush_tlb_vhpt_range (u64 vadr, u64 log_range)
awilliam@10013 188 {
awilliam@10013 189 cpu_flush_vhpt_range (current->processor, vadr, 1UL << log_range);
awilliam@10013 190 ia64_ptcl(vadr, log_range << 2);
awilliam@10013 191 ia64_srlz_i();
awilliam@10013 192 }
awilliam@10013 193
awilliam@10013 194 void domain_flush_vtlb_range (struct domain *d, u64 vadr, u64 addr_range)
awilliam@10013 195 {
awilliam@10013 196 struct vcpu *v;
awilliam@10013 197
awilliam@10013 198 #if 0
awilliam@10013 199 // this only seems to occur at shutdown, but it does occur
awilliam@10013 200 if ((!addr_range) || addr_range & (addr_range - 1)) {
awilliam@10013 201 printf("vhpt_flush_address: weird range, spinning...\n");
awilliam@10013 202 while(1);
awilliam@10013 203 }
awilliam@10013 204 #endif
awilliam@10013 205
awilliam@10013 206 for_each_vcpu (d, v) {
awilliam@10430 207 if (!test_bit(_VCPUF_initialised, &v->vcpu_flags))
awilliam@10430 208 continue;
awilliam@10430 209
awilliam@10013 210 /* Purge TC entries.
awilliam@10013 211 FIXME: clear only if match. */
awilliam@10013 212 vcpu_purge_tr_entry(&PSCBX(v,dtlb));
awilliam@10013 213 vcpu_purge_tr_entry(&PSCBX(v,itlb));
awilliam@10262 214 }
awilliam@10262 215 smp_mb();
awilliam@10013 216
awilliam@10262 217 for_each_vcpu (d, v) {
awilliam@10430 218 if (!test_bit(_VCPUF_initialised, &v->vcpu_flags))
awilliam@10430 219 continue;
awilliam@10430 220
awilliam@10013 221 /* Invalidate VHPT entries. */
awilliam@10013 222 cpu_flush_vhpt_range (v->processor, vadr, addr_range);
awilliam@10013 223 }
awilliam@10262 224 // ptc.ga has release semantics.
awilliam@10013 225
awilliam@10013 226 /* ptc.ga */
awilliam@10013 227 ia64_global_tlb_purge(vadr,vadr+addr_range,PAGE_SHIFT);
awilliam@10013 228 }
awilliam@10013 229
awilliam@10013 230 static void flush_tlb_vhpt_all (struct domain *d)
awilliam@10013 231 {
awilliam@10013 232 /* First VHPT. */
awilliam@10013 233 vhpt_flush ();
awilliam@10013 234
awilliam@10013 235 /* Then mTLB. */
awilliam@10013 236 local_flush_tlb_all ();
awilliam@10013 237 }
awilliam@10013 238
awilliam@10786 239 void domain_flush_tlb_vhpt(struct domain *d)
awilliam@10013 240 {
awilliam@10013 241 /* Very heavy... */
awilliam@10013 242 on_each_cpu ((void (*)(void *))flush_tlb_vhpt_all, d, 1, 1);
awilliam@10013 243 cpus_clear (d->domain_dirty_cpumask);
awilliam@10013 244 }
awilliam@10013 245
awilliam@10013 246 void flush_tlb_mask(cpumask_t mask)
awilliam@10013 247 {
awilliam@10013 248 int cpu;
awilliam@10013 249
awilliam@10013 250 cpu = smp_processor_id();
awilliam@10013 251 if (cpu_isset (cpu, mask)) {
awilliam@10013 252 cpu_clear(cpu, mask);
awilliam@10013 253 flush_tlb_vhpt_all (NULL);
awilliam@10013 254 }
awilliam@10013 255
awilliam@10013 256 if (cpus_empty(mask))
awilliam@10425 257 return;
awilliam@10013 258
awilliam@10013 259 for_each_cpu_mask (cpu, mask)
awilliam@10013 260 smp_call_function_single
awilliam@10013 261 (cpu, (void (*)(void *))flush_tlb_vhpt_all, NULL, 1, 1);
awilliam@10013 262 }
awilliam@10013 263
djm@6864 264 void zero_vhpt_stats(void)
djm@6864 265 {
djm@6864 266 return;
djm@6864 267 }
djm@6864 268
djm@6864 269 int dump_vhpt_stats(char *buf)
djm@6864 270 {
awilliam@10443 271 int i, cpu;
djm@6864 272 char *s = buf;
djm@6864 273
awilliam@10443 274 s += sprintf(s,"VHPT usage (%ld entries):\n",
awilliam@10443 275 (unsigned long) VHPT_NUM_ENTRIES);
awilliam@10443 276
awilliam@10443 277 for_each_present_cpu (cpu) {
awilliam@10443 278 struct vhpt_lf_entry *v = __va(per_cpu(vhpt_paddr, cpu));
awilliam@10443 279 unsigned long vhpt_valid = 0;
awilliam@10443 280
awilliam@10443 281 for (i = 0; i < VHPT_NUM_ENTRIES; i++, v++)
awilliam@10443 282 if (!(v->ti_tag & INVALID_TI_TAG))
awilliam@10443 283 vhpt_valid++;
awilliam@10443 284 s += sprintf(s," cpu %d: %ld\n", cpu, vhpt_valid);
djm@6864 285 }
awilliam@10443 286
djm@6864 287 return s - buf;
djm@6864 288 }