lguest: cache last cpu we ran on.

This optimizes the frobbing of our Switcher map.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2013-04-22 14:10:41 +09:30
parent 86935fc4ee
commit 6d0cda93c0
2 changed files with 55 additions and 26 deletions

View File

@ -17,6 +17,7 @@
struct pgdir { struct pgdir {
unsigned long gpgdir; unsigned long gpgdir;
bool switcher_mapped; bool switcher_mapped;
int last_host_cpu;
pgd_t *pgdir; pgd_t *pgdir;
}; };

View File

@ -7,7 +7,7 @@
* converted Guest pages when running the Guest. * converted Guest pages when running the Guest.
:*/ :*/
/* Copyright (C) Rusty Russell IBM Corporation 2006. /* Copyright (C) Rusty Russell IBM Corporation 2013.
* GPL v2 and any later version */ * GPL v2 and any later version */
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/gfp.h> #include <linux/gfp.h>
@ -731,6 +731,9 @@ static unsigned int new_pgdir(struct lg_cpu *cpu,
/* Release all the non-kernel mappings. */ /* Release all the non-kernel mappings. */
flush_user_mappings(cpu->lg, next); flush_user_mappings(cpu->lg, next);
/* This hasn't run on any CPU at all. */
cpu->lg->pgdirs[next].last_host_cpu = -1;
return next; return next;
} }
@ -790,6 +793,7 @@ static void release_all_pagetables(struct lguest *lg)
for (j = 0; j < PTRS_PER_PGD; j++) for (j = 0; j < PTRS_PER_PGD; j++)
release_pgd(lg->pgdirs[i].pgdir + j); release_pgd(lg->pgdirs[i].pgdir + j);
lg->pgdirs[i].switcher_mapped = false; lg->pgdirs[i].switcher_mapped = false;
lg->pgdirs[i].last_host_cpu = -1;
} }
} }
@ -1086,37 +1090,62 @@ void free_guest_pagetable(struct lguest *lg)
free_page((long)lg->pgdirs[i].pgdir); free_page((long)lg->pgdirs[i].pgdir);
} }
/*H:481
* This clears the Switcher mappings for cpu #i.
*/
static void remove_switcher_percpu_map(struct lg_cpu *cpu, unsigned int i)
{
unsigned long base = switcher_addr + PAGE_SIZE + i * PAGE_SIZE*2;
pte_t *pte;
/* Clear the mappings for both pages. */
pte = find_spte(cpu, base, false, 0, 0);
release_pte(*pte);
set_pte(pte, __pte(0));
pte = find_spte(cpu, base + PAGE_SIZE, false, 0, 0);
release_pte(*pte);
set_pte(pte, __pte(0));
}
/*H:480 /*H:480
* (vi) Mapping the Switcher when the Guest is about to run. * (vi) Mapping the Switcher when the Guest is about to run.
* *
* The Switcher and the two pages for this CPU need to be visible in the * The Switcher and the two pages for this CPU need to be visible in the Guest
* Guest (and not the pages for other CPUs). * (and not the pages for other CPUs).
* *
* The pages have all been allocate * The pages for the pagetables have all been allocated before: we just need
* to make sure the actual PTEs are up-to-date for the CPU we're about to run
* on.
*/ */
void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages) void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages)
{ {
unsigned long base, i; unsigned long base;
struct page *percpu_switcher_page, *regs_page; struct page *percpu_switcher_page, *regs_page;
pte_t *pte; pte_t *pte;
struct pgdir *pgdir = &cpu->lg->pgdirs[cpu->cpu_pgd];
/* Switcher page should always be mapped! */ /* Switcher page should always be mapped by now! */
BUG_ON(!cpu->lg->pgdirs[cpu->cpu_pgd].switcher_mapped); BUG_ON(!pgdir->switcher_mapped);
/* Clear all the Switcher mappings for any other CPUs. */ /*
/* FIXME: This is dumb: update only when Host CPU changes. */ * Remember that we have two pages for each Host CPU, so we can run a
for_each_possible_cpu(i) { * Guest on each CPU without them interfering. We need to make sure
/* Get location of lguest_pages (indexed by Host CPU) */ * those pages are mapped correctly in the Guest, but since we usually
base = switcher_addr + PAGE_SIZE * run on the same CPU, we cache that, and only update the mappings
+ i * sizeof(struct lguest_pages); * when we move.
*/
if (pgdir->last_host_cpu == raw_smp_processor_id())
return;
/* Get shadow PTE for first page (where we put guest regs). */ /* -1 means unknown so we remove everything. */
pte = find_spte(cpu, base, false, 0, 0); if (pgdir->last_host_cpu == -1) {
set_pte(pte, __pte(0)); unsigned int i;
for_each_possible_cpu(i)
/* This is where we put R/O state. */ remove_switcher_percpu_map(cpu, i);
pte = find_spte(cpu, base + PAGE_SIZE, false, 0, 0); } else {
set_pte(pte, __pte(0)); /* We know exactly what CPU mapping to remove. */
remove_switcher_percpu_map(cpu, pgdir->last_host_cpu);
} }
/* /*
@ -1140,18 +1169,17 @@ void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages)
* the Guest: the IDT, GDT and other things it's not supposed to * the Guest: the IDT, GDT and other things it's not supposed to
* change. * change.
*/ */
base += PAGE_SIZE; pte = find_spte(cpu, base + PAGE_SIZE, false, 0, 0);
pte = find_spte(cpu, base, false, 0, 0);
percpu_switcher_page percpu_switcher_page
= lg_switcher_pages[1 + raw_smp_processor_id()*2 + 1]; = lg_switcher_pages[1 + raw_smp_processor_id()*2 + 1];
get_page(percpu_switcher_page); get_page(percpu_switcher_page);
set_pte(pte, mk_pte(percpu_switcher_page, set_pte(pte, mk_pte(percpu_switcher_page,
__pgprot(__PAGE_KERNEL_RO & ~_PAGE_GLOBAL))); __pgprot(__PAGE_KERNEL_RO & ~_PAGE_GLOBAL)));
}
/*:*/
/* pgdir->last_host_cpu = raw_smp_processor_id();
}
/*H:490
* We've made it through the page table code. Perhaps our tired brains are * We've made it through the page table code. Perhaps our tired brains are
* still processing the details, or perhaps we're simply glad it's over. * still processing the details, or perhaps we're simply glad it's over.
* *