lguest: speed up PARAVIRT_LAZY_FLUSH handling
PATCH 1/3 lguest: speed up PARAVIRT_LAZY_FLUSH handling
When Zach Amsden added PARAVIRT_LAZY_FLUSH I didn't realize how often it would get called. We only need to do something if we're actually in lazy mode.
Before:
Time for one context switch via pipe: 10509 (9863 - 18761) Time for one Copy-on-Write fault: 71796 (20625 - 207750) Time to exec client once: 1076218 (1066203 - 1085937) Time for one fork/exit/wait: 1193125 (574750 - 1197750) Time for two PTE updates: 10844 (10659 - 20703)
After:
Time for one context switch via pipe: 6745 (6521 - 13966) Time for one Copy-on-Write fault: 44734 (11468 - 91988) Time to exec client once: 815984 (801218 - 878218) Time for one fork/exit/wait: 1023250 (397687 - 1030375) Time for two PTE updates: 6699 (6475 - 9279)
(Native for comparison):
Time for one context switch via pipe: 4031 (3212 - 4146) Time for one Copy-on-Write fault: 4402 (4388 - 4426) Time to exec client once: 343859 (336859 - 349484) Time for one fork/exit/wait: 120234 (118500 - 136140) Time for two PTE updates: 2269 (2261 - 2272)
drivers/lguest/lguest.c +4 -3
@@ -58,9 +58,10 @@ static enum paravirt_lazy_mode lazy_modestatic enum paravirt_lazy_mode lazy_mode;static void lguest_lazy_mode (enum paravirt_lazy_mode mode) { -if (mode ==PARAVIRT_LAZY_FLUSH ) -hcall (LHCALL_FLUSH_ASYNC ,0 ,0 ,0 ); -else { +if (mode ==PARAVIRT_LAZY_FLUSH ) { +if (unlikely (lazy_mode !=PARAVIRT_LAZY_NONE )) +hcall (LHCALL_FLUSH_ASYNC ,0 ,0 ,0 ); + }else { lazy_mode = mode;if (mode ==PARAVIRT_LAZY_NONE )hcall (LHCALL_FLUSH_ASYNC ,0 ,0 ,0 );
PATCH 2/3 lguest: more lazy_hcalls
Some hypercalls can be batched: lazy_hcall is the wrapper which determines this, so it's pretty harmless to use lazy_hcall() instead of hcall().
Before:
Time for one context switch via pipe: 6745 (6521 - 13966)
After:
Time for one context switch via pipe: 5406 (5170 - 7467)
--- drivers/lguest/lguest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
===================================================================
drivers/lguest/lguest.c +3 -3
@@ -225,7 +225,7 @@ static unsigned long current_cr0, currenstatic unsigned long current_cr0, current_cr3;static void lguest_write_cr0 (unsigned long val) { -hcall (LHCALL_TS , val &8 ,0 ,0 ); +lazy_hcall (LHCALL_TS , val &8 ,0 ,0 ); current_cr0 = val; }
@@ -247,7 +247,7 @@ static unsigned long lguest_read_cr2(voistatic void lguest_write_cr3 (unsigned long cr3) { -hcall (LHCALL_NEW_PGTABLE , cr3,0 ,0 ); +lazy_hcall (LHCALL_NEW_PGTABLE , cr3,0 ,0 ); current_cr3 = cr3; }
@@ -287,7 +287,7 @@ static void lguest_set_pte(pte_t *ptep, *ptep = pteval;/* Don't bother with hypercall before initial setup. */ if (current_cr3) -hcall (LHCALL_FLUSH_TLB ,1 ,0 ,0 ); +lazy_hcall (LHCALL_FLUSH_TLB ,1 ,0 ,0 ); }static void lguest_flush_tlb_single (unsigned long addr)
PATCH 2/3 lguest: more lazy_hcalls
In reply to Rusty RussellSome hypercalls can be batched: lazy_hcall is the wrapper which determines this, so it's pretty harmless to use lazy_hcall() instead of hcall().
Before:
Time for one context switch via pipe: 6745 (6521 - 13966)
After:
Time for one context switch via pipe: 5406 (5170 - 7467)
--- drivers/lguest/lguest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
===================================================================
drivers/lguest/lguest.c +3 -3
@@ -225,7 +225,7 @@ static unsigned long current_cr0, currenstatic unsigned long current_cr0, current_cr3;static void lguest_write_cr0 (unsigned long val) { -hcall (LHCALL_TS , val &8 ,0 ,0 ); +lazy_hcall (LHCALL_TS , val &8 ,0 ,0 ); current_cr0 = val; }
@@ -247,7 +247,7 @@ static unsigned long lguest_read_cr2(voistatic void lguest_write_cr3 (unsigned long cr3) { -hcall (LHCALL_NEW_PGTABLE , cr3,0 ,0 ); +lazy_hcall (LHCALL_NEW_PGTABLE , cr3,0 ,0 ); current_cr3 = cr3; }
@@ -287,7 +287,7 @@ static void lguest_set_pte(pte_t *ptep, *ptep = pteval;/* Don't bother with hypercall before initial setup. */ if (current_cr3) -hcall (LHCALL_FLUSH_TLB ,1 ,0 ,0 ); +lazy_hcall (LHCALL_FLUSH_TLB ,1 ,0 ,0 ); }static void lguest_flush_tlb_single (unsigned long addr)
PATCH 3/3 lguest: faster tls switching
1) When the Guest sets the new TLS entries, we don't need to re-verify the whole GDT.
2) We also don't need to copy all the GDT entries if only the TLS entries have changed.
Before:
Time for one context switch via pipe: 5406 (5170 - 7467)
After:
Time for one context switch via pipe: 4916 (4661 - 7004)
--- drivers/lguest/core.c | 5 ++++- drivers/lguest/lg.h | 2 ++ drivers/lguest/segments.c | 30 ++++++++++++++++++------------ 3 files changed, 24 insertions(+), 13 deletions(-)
drivers/lguest/core.c +3 -0
@@ -278,6 +278,9 @@ static void copy_in_guest_info(struct lg/* Copy all GDT entries but the TSS. */ if (lg->changed &CHANGED_GDT )copy_gdt (lg, pages->state.guest_gdt); +/* If only the TLS entries have changed, copy them. */ +else if (lg->changed &CHANGED_GDT_TLS ) +copy_gdt_tls (lg, pages->state.guest_gdt); lg->changed =0 ; }
drivers/lguest/lg.h +2 -0
@@ -115,6 +115,7 @@ struct lguest_pages#define CHANGED_IDT 1#define CHANGED_GDT 2 +#define CHANGED_GDT_TLS 4/* Actually a subset of CHANGED_GDT */ #define CHANGED_ALL 3/* The private info the thread maintains about the guest. */
@@ -202,6 +203,7 @@ void load_guest_gdt(struct lguest *lg, uvoid load_guest_gdt (struct lguest *lg,unsigned long table,u32 num);void guest_load_tls (struct lguest *lg,unsigned long tls_array);void copy_gdt (const struct lguest *lg,struct desc_struct *gdt); +void copy_gdt_tls (const struct lguest *lg,struct desc_struct *gdt);/* page_tables.c: */ int init_guest_pagetable (struct lguest *lg,unsigned long pgtable);
drivers/lguest/segments.c +15 -5
@@ -34,11 +34,11 @@ static void check_segment_use(struct lgukill_guest (lg,"Removed live GDT entry %u" , desc); } -static void fixup_gdt_table (struct lguest *lg) +static void fixup_gdt_table (struct lguest *lg,unsigned start,unsigned end) {unsigned int i; -for (i =0 ; i <ARRAY_SIZE (lg->gdt); i++) { +for (i = start; i < end; i++) {/* We never copy these ones to real gdt */ if (ignored_gdt (i))continue ;
@@ -86,6 +86,16 @@ void setup_guest_gdt(struct lguest *lg) lg->gdt[GDT_ENTRY_KERNEL_DS ].b |= (GUEST_PL <<13 ); } +/* This is a fast version for the common case where only the three TLS entries +* have changed. */ +void copy_gdt_tls (const struct lguest *lg,struct desc_struct *gdt) +{ +unsigned int i; + +for (i =GDT_ENTRY_TLS_MIN ; i <=GDT_ENTRY_TLS_MAX ; i++) + gdt[i] = lg->gdt[i]; +} +void copy_gdt (const struct lguest *lg,struct desc_struct *gdt) {unsigned int i;
@@ -101,7 +111,7 @@ void load_guest_gdt(struct lguest *lg, ukill_guest (lg,"too many gdt entries %i" , num);lgread (lg, lg->gdt, table, num *sizeof (lg->gdt[0 ])); -fixup_gdt_table (lg); +fixup_gdt_table (lg,0 ,ARRAY_SIZE (lg->gdt)); lg->changed |=CHANGED_GDT ; }
@@ -110,6 +120,6 @@ void guest_load_tls(struct lguest *lg, ustruct desc_struct *tls = &lg->gdt[GDT_ENTRY_TLS_MIN ];lgread (lg, tls, gtls,sizeof (*tls)*GDT_ENTRY_TLS_ENTRIES ); -fixup_gdt_table (lg); - lg->changed |=CHANGED_GDT ; +fixup_gdt_table (lg,GDT_ENTRY_TLS_MIN ,GDT_ENTRY_TLS_MAX +1 ); + lg->changed |=CHANGED_GDT_TLS ; }
PATCH 3/3 lguest: faster tls switching
In reply to Rusty Russell1) When the Guest sets the new TLS entries, we don't need to re-verify the whole GDT.
2) We also don't need to copy all the GDT entries if only the TLS entries have changed.
Before:
Time for one context switch via pipe: 5406 (5170 - 7467)
After:
Time for one context switch via pipe: 4916 (4661 - 7004)
--- drivers/lguest/core.c | 5 ++++- drivers/lguest/lg.h | 2 ++ drivers/lguest/segments.c | 30 ++++++++++++++++++------------ 3 files changed, 24 insertions(+), 13 deletions(-)
drivers/lguest/core.c +3 -0
@@ -278,6 +278,9 @@ static void copy_in_guest_info(struct lg/* Copy all GDT entries but the TSS. */ if (lg->changed &CHANGED_GDT )copy_gdt (lg, pages->state.guest_gdt); +/* If only the TLS entries have changed, copy them. */ +else if (lg->changed &CHANGED_GDT_TLS ) +copy_gdt_tls (lg, pages->state.guest_gdt); lg->changed =0 ; }
drivers/lguest/lg.h +2 -0
@@ -115,6 +115,7 @@ struct lguest_pages#define CHANGED_IDT 1#define CHANGED_GDT 2 +#define CHANGED_GDT_TLS 4/* Actually a subset of CHANGED_GDT */ #define CHANGED_ALL 3/* The private info the thread maintains about the guest. */
@@ -202,6 +203,7 @@ void load_guest_gdt(struct lguest *lg, uvoid load_guest_gdt (struct lguest *lg,unsigned long table,u32 num);void guest_load_tls (struct lguest *lg,unsigned long tls_array);void copy_gdt (const struct lguest *lg,struct desc_struct *gdt); +void copy_gdt_tls (const struct lguest *lg,struct desc_struct *gdt);/* page_tables.c: */ int init_guest_pagetable (struct lguest *lg,unsigned long pgtable);
drivers/lguest/segments.c +15 -5
@@ -34,11 +34,11 @@ static void check_segment_use(struct lgukill_guest (lg,"Removed live GDT entry %u" , desc); } -static void fixup_gdt_table (struct lguest *lg) +static void fixup_gdt_table (struct lguest *lg,unsigned start,unsigned end) {unsigned int i; -for (i =0 ; i <ARRAY_SIZE (lg->gdt); i++) { +for (i = start; i < end; i++) {/* We never copy these ones to real gdt */ if (ignored_gdt (i))continue ;
@@ -86,6 +86,16 @@ void setup_guest_gdt(struct lguest *lg) lg->gdt[GDT_ENTRY_KERNEL_DS ].b |= (GUEST_PL <<13 ); } +/* This is a fast version for the common case where only the three TLS entries +* have changed. */ +void copy_gdt_tls (const struct lguest *lg,struct desc_struct *gdt) +{ +unsigned int i; + +for (i =GDT_ENTRY_TLS_MIN ; i <=GDT_ENTRY_TLS_MAX ; i++) + gdt[i] = lg->gdt[i]; +} +void copy_gdt (const struct lguest *lg,struct desc_struct *gdt) {unsigned int i;
@@ -101,7 +111,7 @@ void load_guest_gdt(struct lguest *lg, ukill_guest (lg,"too many gdt entries %i" , num);lgread (lg, lg->gdt, table, num *sizeof (lg->gdt[0 ])); -fixup_gdt_table (lg); +fixup_gdt_table (lg,0 ,ARRAY_SIZE (lg->gdt)); lg->changed |=CHANGED_GDT ; }
@@ -110,6 +120,6 @@ void guest_load_tls(struct lguest *lg, ustruct desc_struct *tls = &lg->gdt[GDT_ENTRY_TLS_MIN ];lgread (lg, tls, gtls,sizeof (*tls)*GDT_ENTRY_TLS_ENTRIES ); -fixup_gdt_table (lg); - lg->changed |=CHANGED_GDT ; +fixup_gdt_table (lg,GDT_ENTRY_TLS_MIN ,GDT_ENTRY_TLS_MAX +1 ); + lg->changed |=CHANGED_GDT_TLS ; }