]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
Merge branch 'x86/mem' into perf/core
authorIngo Molnar <mingo@elte.hu>
Wed, 18 May 2011 18:59:27 +0000 (20:59 +0200)
committerIngo Molnar <mingo@elte.hu>
Wed, 18 May 2011 18:59:30 +0000 (20:59 +0200)
Merge reason: memcpy_64.S changes an assumption perf bench has, so merge this
              here so we can fix it.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/include/asm/alternative-asm.h
arch/x86/include/asm/cpufeature.h
arch/x86/include/asm/uaccess.h
arch/x86/kernel/alternative.c
arch/x86/kernel/cpu/common.c
arch/x86/kernel/cpu/intel.c
arch/x86/lib/clear_page_64.S
arch/x86/lib/copy_user_64.S
arch/x86/lib/memcpy_64.S
arch/x86/lib/memmove_64.S
arch/x86/lib/memset_64.S

index a63a68be1cce2258daf9cadf0d465b291296c930..94d420b360d11cdfbbd4a39f13c19bd8d76fc7e0 100644 (file)
        .endm
 #endif
 
+.macro altinstruction_entry orig alt feature orig_len alt_len
+       .align 8
+       .quad \orig
+       .quad \alt
+       .word \feature
+       .byte \orig_len
+       .byte \alt_len
+.endm
+
 #endif  /*  __ASSEMBLY__  */
index 91f3e087cf21817704c68f24379edc4cf31a6885..7f2f7b12329301a5f62fb4f15b4201ba236f4c00 100644 (file)
 
 /* Intel-defined CPU features, CPUID level 0x00000007:0 (ebx), word 9 */
 #define X86_FEATURE_FSGSBASE   (9*32+ 0) /* {RD/WR}{FS/GS}BASE instructions*/
+#define X86_FEATURE_ERMS       (9*32+ 9) /* Enhanced REP MOVSB/STOSB */
 
 #if defined(__KERNEL__) && !defined(__ASSEMBLY__)
 
index abd3e0ea762ac739b054ddd1922efba71017a742..99f0ad753f32c482f9f5b222c84cee881696b628 100644 (file)
@@ -42,7 +42,7 @@
  * Returns 0 if the range is valid, nonzero otherwise.
  *
  * This is equivalent to the following test:
- * (u33)addr + (u33)size >= (u33)current->addr_limit.seg (u65 for x86_64)
+ * (u33)addr + (u33)size > (u33)current->addr_limit.seg (u65 for x86_64)
  *
  * This needs 33-bit (65-bit for x86_64) arithmetic. We have a carry...
  */
index 651454b0c8110a08ac493e1976c7621598b8d7f7..1eeeafcb4410f06bb21850fb2cf77906b3848580 100644 (file)
@@ -210,6 +210,15 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
        u8 insnbuf[MAX_PATCH_LEN];
 
        DPRINTK("%s: alt table %p -> %p\n", __func__, start, end);
+       /*
+        * The scan order should be from start to end. A later scanned
+        * alternative code can overwrite a previous scanned alternative code.
+        * Some kernel functions (e.g. memcpy, memset, etc) use this order to
+        * patch code.
+        *
+        * So be careful if you want to change the scan order to any other
+        * order.
+        */
        for (a = start; a < end; a++) {
                u8 *instr = a->instr;
                BUG_ON(a->replacementlen > a->instrlen);
index e2ced0074a45c1ffee1eeb1a7e8d56657824847f..173f3a3fa1a63dcacc5c1aec67341e5f19f118f6 100644 (file)
@@ -565,8 +565,7 @@ void __cpuinit get_cpu_cap(struct cpuinfo_x86 *c)
 
                cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx);
 
-               if (eax > 0)
-                       c->x86_capability[9] = ebx;
+               c->x86_capability[9] = ebx;
        }
 
        /* AMD-defined flags: level 0x80000001 */
index df86bc8c859d6ff8596759e7a54258fd9764d7d6..fc73a34ba8c9c1cb93c1086845090d03782381b5 100644 (file)
 
 static void __cpuinit early_init_intel(struct cpuinfo_x86 *c)
 {
+       u64 misc_enable;
+
        /* Unmask CPUID levels if masked: */
        if (c->x86 > 6 || (c->x86 == 6 && c->x86_model >= 0xd)) {
-               u64 misc_enable;
-
                rdmsrl(MSR_IA32_MISC_ENABLE, misc_enable);
 
                if (misc_enable & MSR_IA32_MISC_ENABLE_LIMIT_CPUID) {
@@ -118,8 +118,6 @@ static void __cpuinit early_init_intel(struct cpuinfo_x86 *c)
         * (model 2) with the same problem.
         */
        if (c->x86 == 15) {
-               u64 misc_enable;
-
                rdmsrl(MSR_IA32_MISC_ENABLE, misc_enable);
 
                if (misc_enable & MSR_IA32_MISC_ENABLE_FAST_STRING) {
@@ -130,6 +128,19 @@ static void __cpuinit early_init_intel(struct cpuinfo_x86 *c)
                }
        }
 #endif
+
+       /*
+        * If fast string is not enabled in IA32_MISC_ENABLE for any reason,
+        * clear the fast string and enhanced fast string CPU capabilities.
+        */
+       if (c->x86 > 6 || (c->x86 == 6 && c->x86_model >= 0xd)) {
+               rdmsrl(MSR_IA32_MISC_ENABLE, misc_enable);
+               if (!(misc_enable & MSR_IA32_MISC_ENABLE_FAST_STRING)) {
+                       printk(KERN_INFO "Disabled fast string operations\n");
+                       setup_clear_cpu_cap(X86_FEATURE_REP_GOOD);
+                       setup_clear_cpu_cap(X86_FEATURE_ERMS);
+               }
+       }
 }
 
 #ifdef CONFIG_X86_32
index aa4326bfb24a1dc6dbe2b131dd7c7c19bb58d58d..f2145cfa12a66830e834718340c4cc88e64a731a 100644 (file)
@@ -1,5 +1,6 @@
 #include <linux/linkage.h>
 #include <asm/dwarf2.h>
+#include <asm/alternative-asm.h>
 
 /*
  * Zero a page.        
@@ -14,6 +15,15 @@ ENTRY(clear_page_c)
        CFI_ENDPROC
 ENDPROC(clear_page_c)
 
+ENTRY(clear_page_c_e)
+       CFI_STARTPROC
+       movl $4096,%ecx
+       xorl %eax,%eax
+       rep stosb
+       ret
+       CFI_ENDPROC
+ENDPROC(clear_page_c_e)
+
 ENTRY(clear_page)
        CFI_STARTPROC
        xorl   %eax,%eax
@@ -38,21 +48,26 @@ ENTRY(clear_page)
 .Lclear_page_end:
 ENDPROC(clear_page)
 
-       /* Some CPUs run faster using the string instructions.
-          It is also a lot simpler. Use this when possible */
+       /*
+        * Some CPUs support enhanced REP MOVSB/STOSB instructions.
+        * It is recommended to use this when possible.
+        * If enhanced REP MOVSB/STOSB is not available, try to use fast string.
+        * Otherwise, use original function.
+        *
+        */
 
 #include <asm/cpufeature.h>
 
        .section .altinstr_replacement,"ax"
 1:     .byte 0xeb                                      /* jmp <disp8> */
        .byte (clear_page_c - clear_page) - (2f - 1b)   /* offset */
-2:
+2:     .byte 0xeb                                      /* jmp <disp8> */
+       .byte (clear_page_c_e - clear_page) - (3f - 2b) /* offset */
+3:
        .previous
        .section .altinstructions,"a"
-       .align 8
-       .quad clear_page
-       .quad 1b
-       .word X86_FEATURE_REP_GOOD
-       .byte .Lclear_page_end - clear_page
-       .byte 2b - 1b
+       altinstruction_entry clear_page,1b,X86_FEATURE_REP_GOOD,\
+                            .Lclear_page_end-clear_page, 2b-1b
+       altinstruction_entry clear_page,2b,X86_FEATURE_ERMS,   \
+                            .Lclear_page_end-clear_page,3b-2b
        .previous
index 99e482615195484e9da7c6152ee0fb1969488067..024840266ba0d94144697de13194417d0427d22f 100644 (file)
 #include <asm/asm-offsets.h>
 #include <asm/thread_info.h>
 #include <asm/cpufeature.h>
+#include <asm/alternative-asm.h>
 
-       .macro ALTERNATIVE_JUMP feature,orig,alt
+/*
+ * By placing feature2 after feature1 in altinstructions section, we logically
+ * implement:
+ * If CPU has feature2, jmp to alt2 is used
+ * else if CPU has feature1, jmp to alt1 is used
+ * else jmp to orig is used.
+ */
+       .macro ALTERNATIVE_JUMP feature1,feature2,orig,alt1,alt2
 0:
        .byte 0xe9      /* 32bit jump */
        .long \orig-1f  /* by default jump to orig */
 1:
        .section .altinstr_replacement,"ax"
 2:     .byte 0xe9                      /* near jump with 32bit immediate */
-       .long \alt-1b /* offset */   /* or alternatively to alt */
+       .long \alt1-1b /* offset */   /* or alternatively to alt1 */
+3:     .byte 0xe9                      /* near jump with 32bit immediate */
+       .long \alt2-1b /* offset */   /* or alternatively to alt2 */
        .previous
+
        .section .altinstructions,"a"
-       .align 8
-       .quad  0b
-       .quad  2b
-       .word  \feature                 /* when feature is set */
-       .byte  5
-       .byte  5
+       altinstruction_entry 0b,2b,\feature1,5,5
+       altinstruction_entry 0b,3b,\feature2,5,5
        .previous
        .endm
 
@@ -72,8 +79,10 @@ ENTRY(_copy_to_user)
        addq %rdx,%rcx
        jc bad_to_user
        cmpq TI_addr_limit(%rax),%rcx
-       jae bad_to_user
-       ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string
+       ja bad_to_user
+       ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,X86_FEATURE_ERMS, \
+               copy_user_generic_unrolled,copy_user_generic_string,    \
+               copy_user_enhanced_fast_string
        CFI_ENDPROC
 ENDPROC(_copy_to_user)
 
@@ -85,8 +94,10 @@ ENTRY(_copy_from_user)
        addq %rdx,%rcx
        jc bad_from_user
        cmpq TI_addr_limit(%rax),%rcx
-       jae bad_from_user
-       ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string
+       ja bad_from_user
+       ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,X86_FEATURE_ERMS, \
+               copy_user_generic_unrolled,copy_user_generic_string,    \
+               copy_user_enhanced_fast_string
        CFI_ENDPROC
 ENDPROC(_copy_from_user)
 
@@ -255,3 +266,37 @@ ENTRY(copy_user_generic_string)
        .previous
        CFI_ENDPROC
 ENDPROC(copy_user_generic_string)
+
+/*
+ * Some CPUs are adding enhanced REP MOVSB/STOSB instructions.
+ * It's recommended to use enhanced REP MOVSB/STOSB if it's enabled.
+ *
+ * Input:
+ * rdi destination
+ * rsi source
+ * rdx count
+ *
+ * Output:
+ * eax uncopied bytes or 0 if successful.
+ */
+ENTRY(copy_user_enhanced_fast_string)
+       CFI_STARTPROC
+       andl %edx,%edx
+       jz 2f
+       movl %edx,%ecx
+1:     rep
+       movsb
+2:     xorl %eax,%eax
+       ret
+
+       .section .fixup,"ax"
+12:    movl %ecx,%edx          /* ecx is zerorest also */
+       jmp copy_user_handle_tail
+       .previous
+
+       .section __ex_table,"a"
+       .align 8
+       .quad 1b,12b
+       .previous
+       CFI_ENDPROC
+ENDPROC(copy_user_enhanced_fast_string)
index 75ef61e35e38aee1cf62b05a48f1add62b148f98..daab21dae2d17ac407f7a0c862b63a08e41b8785 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <asm/cpufeature.h>
 #include <asm/dwarf2.h>
+#include <asm/alternative-asm.h>
 
 /*
  * memcpy - Copy a memory block.
 .Lmemcpy_e:
        .previous
 
+/*
+ * memcpy_c_e() - enhanced fast string memcpy. This is faster and simpler than
+ * memcpy_c. Use memcpy_c_e when possible.
+ *
+ * This gets patched over the unrolled variant (below) via the
+ * alternative instructions framework:
+ */
+       .section .altinstr_replacement, "ax", @progbits
+.Lmemcpy_c_e:
+       movq %rdi, %rax
+
+       movl %edx, %ecx
+       rep movsb
+       ret
+.Lmemcpy_e_e:
+       .previous
+
 ENTRY(__memcpy)
 ENTRY(memcpy)
        CFI_STARTPROC
@@ -171,21 +189,22 @@ ENDPROC(memcpy)
 ENDPROC(__memcpy)
 
        /*
-        * Some CPUs run faster using the string copy instructions.
-        * It is also a lot simpler. Use this when possible:
-        */
-
-       .section .altinstructions, "a"
-       .align 8
-       .quad memcpy
-       .quad .Lmemcpy_c
-       .word X86_FEATURE_REP_GOOD
-
-       /*
+        * Some CPUs are adding enhanced REP MOVSB/STOSB feature
+        * If the feature is supported, memcpy_c_e() is the first choice.
+        * If enhanced rep movsb copy is not available, use fast string copy
+        * memcpy_c() when possible. This is faster and code is simpler than
+        * original memcpy().
+        * Otherwise, original memcpy() is used.
+        * In .altinstructions section, ERMS feature is placed after REG_GOOD
+         * feature to implement the right patch order.
+        *
         * Replace only beginning, memcpy is used to apply alternatives,
         * so it is silly to overwrite itself with nops - reboot is the
         * only outcome...
         */
-       .byte .Lmemcpy_e - .Lmemcpy_c
-       .byte .Lmemcpy_e - .Lmemcpy_c
+       .section .altinstructions, "a"
+       altinstruction_entry memcpy,.Lmemcpy_c,X86_FEATURE_REP_GOOD,\
+                            .Lmemcpy_e-.Lmemcpy_c,.Lmemcpy_e-.Lmemcpy_c
+       altinstruction_entry memcpy,.Lmemcpy_c_e,X86_FEATURE_ERMS, \
+                            .Lmemcpy_e_e-.Lmemcpy_c_e,.Lmemcpy_e_e-.Lmemcpy_c_e
        .previous
index 0ecb8433e5a8e2f62c0a678da69ccd16f233b304..d0ec9c2936d75fb6e7c908c00f2359fd0c366dd5 100644 (file)
@@ -8,6 +8,7 @@
 #define _STRING_C
 #include <linux/linkage.h>
 #include <asm/dwarf2.h>
+#include <asm/cpufeature.h>
 
 #undef memmove
 
@@ -24,6 +25,7 @@
  */
 ENTRY(memmove)
        CFI_STARTPROC
+
        /* Handle more 32bytes in loop */
        mov %rdi, %rax
        cmp $0x20, %rdx
@@ -31,8 +33,13 @@ ENTRY(memmove)
 
        /* Decide forward/backward copy mode */
        cmp %rdi, %rsi
-       jb      2f
+       jge .Lmemmove_begin_forward
+       mov %rsi, %r8
+       add %rdx, %r8
+       cmp %rdi, %r8
+       jg 2f
 
+.Lmemmove_begin_forward:
        /*
         * movsq instruction have many startup latency
         * so we handle small size by general register.
@@ -78,6 +85,8 @@ ENTRY(memmove)
        rep movsq
        movq %r11, (%r10)
        jmp 13f
+.Lmemmove_end_forward:
+
        /*
         * Handle data backward by movsq.
         */
@@ -194,4 +203,22 @@ ENTRY(memmove)
 13:
        retq
        CFI_ENDPROC
+
+       .section .altinstr_replacement,"ax"
+.Lmemmove_begin_forward_efs:
+       /* Forward moving data. */
+       movq %rdx, %rcx
+       rep movsb
+       retq
+.Lmemmove_end_forward_efs:
+       .previous
+
+       .section .altinstructions,"a"
+       .align 8
+       .quad .Lmemmove_begin_forward
+       .quad .Lmemmove_begin_forward_efs
+       .word X86_FEATURE_ERMS
+       .byte .Lmemmove_end_forward-.Lmemmove_begin_forward
+       .byte .Lmemmove_end_forward_efs-.Lmemmove_begin_forward_efs
+       .previous
 ENDPROC(memmove)
index 09d3442696522e19aea8adb9efdbfd051d8d57aa..79bd454b78a36fa91498d71e57018e7c949223d5 100644 (file)
@@ -2,9 +2,13 @@
 
 #include <linux/linkage.h>
 #include <asm/dwarf2.h>
+#include <asm/cpufeature.h>
+#include <asm/alternative-asm.h>
 
 /*
- * ISO C memset - set a memory block to a byte value.
+ * ISO C memset - set a memory block to a byte value. This function uses fast
+ * string to get better performance than the original function. The code is
+ * simpler and shorter than the orignal function as well.
  *     
  * rdi   destination
  * rsi   value (char) 
 .Lmemset_e:
        .previous
 
+/*
+ * ISO C memset - set a memory block to a byte value. This function uses
+ * enhanced rep stosb to override the fast string function.
+ * The code is simpler and shorter than the fast string function as well.
+ *
+ * rdi   destination
+ * rsi   value (char)
+ * rdx   count (bytes)
+ *
+ * rax   original destination
+ */
+       .section .altinstr_replacement, "ax", @progbits
+.Lmemset_c_e:
+       movq %rdi,%r9
+       movb %sil,%al
+       movl %edx,%ecx
+       rep stosb
+       movq %r9,%rax
+       ret
+.Lmemset_e_e:
+       .previous
+
 ENTRY(memset)
 ENTRY(__memset)
        CFI_STARTPROC
@@ -112,16 +138,20 @@ ENTRY(__memset)
 ENDPROC(memset)
 ENDPROC(__memset)
 
-       /* Some CPUs run faster using the string instructions.
-          It is also a lot simpler. Use this when possible */
-
-#include <asm/cpufeature.h>
-
+       /* Some CPUs support enhanced REP MOVSB/STOSB feature.
+        * It is recommended to use this when possible.
+        *
+        * If enhanced REP MOVSB/STOSB feature is not available, use fast string
+        * instructions.
+        *
+        * Otherwise, use original memset function.
+        *
+        * In .altinstructions section, ERMS feature is placed after REG_GOOD
+         * feature to implement the right patch order.
+        */
        .section .altinstructions,"a"
-       .align 8
-       .quad memset
-       .quad .Lmemset_c
-       .word X86_FEATURE_REP_GOOD
-       .byte .Lfinal - memset
-       .byte .Lmemset_e - .Lmemset_c
+       altinstruction_entry memset,.Lmemset_c,X86_FEATURE_REP_GOOD,\
+                            .Lfinal-memset,.Lmemset_e-.Lmemset_c
+       altinstruction_entry memset,.Lmemset_c_e,X86_FEATURE_ERMS, \
+                            .Lfinal-memset,.Lmemset_e_e-.Lmemset_c_e
        .previous