Skip to contentSkip to conversation

Alternative patching for prefetches & cleanup

PATCH In review Andi Kleen 4 participants 7 messages Apr 27, 2003

lore
Share & tools
lore

Andi Kleen

author
#0 lore raw

PATCH 1/1 Alternative patching for prefetches & cleanup

This patch extends and cleans up the early generic CPU alternative patching.

It adds automatic patching for (SSE) prefetches and 3dnow! write prefetches. This is one step towards making the Athlon kernel work on Intel CPUs again. It's not fully finished yet because the memcpy still uses 3dnow!.

I had to split the alternative section into two for this. The reason is that for big kernels gas or ld would sometimes add a few padding bytes after the replacement instruction in .altinstructions, and that would confuse the patcher who relied on knowing the exact length of the instruction. Now the instructions are in another section with a pointer from the main patch record.

It also adds nop types for various CPUs straight from their optimization manuals. Now you can always get the fastest nops for K7,K8 and Intel. I moved them into the include files to make it easy to use them for padding alternative()s. Some cleanups in the patch function to use this.

Removal of the now obsolete X86_PREFETCH and X86_SSE2 options.

arch/i386/kernel/setup.c renamed from arch/i386/kernel/setup.c-o +19 -18
diff -u linux-gencpu/arch/i386/kernel/setup.c-o linux-gencpu/arch/i386/kernel/setup.c
@@ -795,41 +795,42 @@
 		pci_mem_start = low_mem_size;
 }
 
+asm("nops: " 
+    ASM_NOP1 ASM_NOP2 ASM_NOP3 ASM_NOP4 ASM_NOP5 ASM_NOP6
+    ASM_NOP7 ASM_NOP8); 
+
+static inline unsigned char *findnop(int num) 
+{
+	extern unsigned char nops[];
+	int i, k = 0;
+	for (i = 1; i < num; i++) 
+		k += i; 
+	return nops + k;
+} 
+
 /* Replace instructions with better alternatives for this CPU type.
 
    This runs before SMP is initialized to avoid SMP problems with
    self modifying code. This implies that assymetric systems where
    APs have less capabilities than the boot processor are not handled. 
-    
    In this case boot with "noreplacement". */ 
 void apply_alternatives(void *start, void *end) 
 { 
 	struct alt_instr *a; 
 	int diff, i, k;
 
-	for (a = start; a < (struct alt_instr *)end; 
-	     a = (void *)ALIGN((unsigned long)(a + 1) + a->instrlen, 4)) { 
+	for (a = start; (void *)a < end; a++) { 
 		if (!boot_cpu_has(a->cpuid))
 			continue;
 		BUG_ON(a->replacementlen > a->instrlen); 
 		memcpy(a->instr, a->replacement, a->replacementlen); 
 		diff = a->instrlen - a->replacementlen; 
+		/* Pad the rest with nops */
 		for (i = a->replacementlen; diff > 0; diff -= k, i += k) {
-			static const char *nops[] = {
-				0,
-				"\x90",
-#if CONFIG_MK7 || CONFIG_MK8
-				"\x66\x90",
-				"\x66\x66\x90",
-				"\x66\x66\x66\x90",
-#else
-				"\x89\xf6",
-				"\x8d\x76\x00",
-				"\x8d\x74\x26\x00",
-#endif
-			};
-			k = min_t(int, diff, ARRAY_SIZE(nops)); 
-			memcpy(a->instr + i, nops[k], k); 
+			k = diff;
+			if (k > ASM_NOP_MAX)
+				k = ASM_NOP_MAX;
+			memcpy(a->instr + i, findnop(k), k); 
 		} 
 	}
 } 
arch/i386/Kconfig renamed from arch/i386/Kconfig-o +0 -10
diff -u linux-gencpu/arch/i386/Kconfig-o linux-gencpu/arch/i386/Kconfig
@@ -363,16 +370,6 @@
 	depends on MWINCHIP3D || MWINCHIP2 || MWINCHIPC6
 	default y
 
-config X86_PREFETCH
-	bool
-	depends on MPENTIUMIII || MPENTIUM4 || MVIAC3_2
-	default y
-
-config X86_SSE2
-	bool
-	depends on MK8 || MPENTIUM4
-	default y
-
 config HUGETLB_PAGE
 	bool "Huge TLB Page Support"
 	help
arch/i386/vmlinux.lds.S renamed from arch/i386/vmlinux.lds.S-o +1 -0
diff -u linux-gencpu/arch/i386/vmlinux.lds.S-o linux-gencpu/arch/i386/vmlinux.lds.S
@@ -85,6 +85,7 @@
   __alt_instructions = .;
   .altinstructions : { *(.altinstructions) } 
   __alt_instructions_end = .; 
+ .altinstr_replacement : { *(.altinstr_replacement) }
   . = ALIGN(4096);
   __initramfs_start = .;
   .init.ramfs : { *(.init.ramfs) }
include/asm-i386/processor.h renamed from include/asm-i386/processor.h-o +43 -13

81 lines not on this page · raw message

include/asm-i386/system.h renamed from include/asm-i386/system.h-o +29 -1
diff -u linux-gencpu/include/asm-i386/system.h-o linux-gencpu/include/asm-i386/system.h
@@ -279,10 +279,11 @@
 
 struct alt_instr { 
 	u8 *instr; 		/* original instruction */
+	u8 *replacement;
 	u8  cpuid;		/* cpuid bit set for replacement */
 	u8  instrlen;		/* length of original instruction */
 	u8  replacementlen; 	/* length of new instruction, <= instrlen */ 
-	u8  replacement[0];   	/* new instruction */
+	u8  pad;
 }; 
 
 /* 
@@ -302,13 +303,40 @@
 		      ".section .altinstructions,\"a\"\n"     	     \
 		      "  .align 4\n"				       \
 		      "  .long 661b\n"            /* label */          \
+		      "  .long 663f\n"		  /* new instruction */ 	\
 		      "  .byte %c0\n"             /* feature bit */    \
 		      "  .byte 662b-661b\n"       /* sourcelen */      \
 		      "  .byte 664f-663f\n"       /* replacementlen */ \
+		      ".previous\n"						\
+		      ".section .altinstr_replacement,\"ax\"\n"			\
 		      "663:\n\t" newinstr "\n664:\n"   /* replacement */    \
 		      ".previous" :: "i" (feature) : "memory")  
 
 /*
+ * Alternative inline assembly with input.
+ * 
+ * Pecularities:
+ * No memory clobber here. 
+ * Argument numbers start with 1.
+ * Best is to use constraints that are fixed size (like (%1) ... "r")
+ * If you use variable sized constraints like "m" or "g" in the 
+ * replacement maake sure to pad to the worst case length.
+ */
+#define alternative_input(oldinstr, newinstr, feature, input)			\
+	asm volatile ("661:\n\t" oldinstr "\n662:\n"				\
+		      ".section .altinstructions,\"a\"\n"			\
+		      "  .align 4\n"						\
+		      "  .long 661b\n"            /* label */			\
+		      "  .long 663f\n"		  /* new instruction */ 	\
+		      "  .byte %c0\n"             /* feature bit */		\
+		      "  .byte 662b-661b\n"       /* sourcelen */		\
+		      "  .byte 664f-663f\n"       /* replacementlen */ 		\
+		      ".previous\n"						\
+		      ".section .altinstr_replacement,\"ax\"\n"			\
+		      "663:\n\t" newinstr "\n664:\n"   /* replacement */ 	\
+		      ".previous" :: "i" (feature), input)  
+
+/*
  * Force strict CPU ordering.
  * And yes, this is required on UP too when we're talking
  * to devices.
#1 lore raw
Quoted 5 lines · Andi Kleen

It also adds nop types for various CPUs straight from their optimization manuals. Now you can always get the fastest nops for K7,K8 and Intel. I moved them into the include files to make it easy to use them for padding alternative()s. Some cleanups in the patch function to use this.

Please, this part I definitely want cleaned up.

arch/i386/kernel/setup.c line 801

Quoted 9 lines

+++ linux-gencpu/arch/i386/kernel/setup.c 2003-04-27 04:12:32.000000000 +0200

arch/i386/kernel/setup.c renamed from arch/i386/kernel/setup.c-o +4 -0
@@ -795,41 +795,42 @@
 		pci_mem_start = low_mem_size;
 }
 
+asm("nops: " 
+    ASM_NOP1 ASM_NOP2 ASM_NOP3 ASM_NOP4 ASM_NOP5 ASM_NOP6
+    ASM_NOP7 ASM_NOP8); 
+

This in particular is just too ugly for words. Why can't you just have a

	static const char *intel_nops[] = {
		NULL,
		INOP1, INOP2, INOP3, INOP4, INOP5, INOP6, INOP7, INOP8
	};

	static const char *k7_nops[] = {
		NULL,
		K7NOP1, K7NOP2, ...
	}

	....

	/*
	 * This will be overridden at boot once we find out what kind of 
	 * CPU we actually have
	 */
	static char **nops = intel_nops;

and then just use

	.. nops[size] ..

when you want a nop of size "size".

Linus

author
#2 lore raw
Quoted 11 lines

arch/i386/kernel/setup.c line 801

Quoted 9 lines

+++ linux-gencpu/arch/i386/kernel/setup.c 2003-04-27 04:12:32.000000000 +0200

arch/i386/kernel/setup.c renamed from arch/i386/kernel/setup.c-o +4 -0
@@ -795,41 +795,42 @@
 		pci_mem_start = low_mem_size;
 }
 
+asm("nops: " 
+    ASM_NOP1 ASM_NOP2 ASM_NOP3 ASM_NOP4 ASM_NOP5 ASM_NOP6
+    ASM_NOP7 ASM_NOP8); 
+

This in particular is just too ugly for words. Why can't you just have a

Hmm. I thought using the Fibonaci sequence for this was clever :-)

But ok I'll change it to the array.

-Andi

#3 lore raw
Quoted 2 lines · Andi Kleen

Hmm. I thought using the Fibonaci sequence for this was clever :-)

That's not the fibonacci sequence, that's just a regular sigma(i) (i=1..n) sequence. And if you were to generate the sequence numbers at compile-time I might agree with you, if you also were to avoid using inline asms.

Linus

author
#4 lore raw
Quoted 9 lines · Linus Torvalds
Quoted 2 lines · Andi Kleen

Hmm. I thought using the Fibonaci sequence for this was clever :-)

That's not the fibonacci sequence, that's just a regular sigma(i) (i=1..n) sequence. And if you were to generate the sequence numbers at compile-time I might agree with you, if you also were to avoid using inline asms.

True %)

The real reason I did it was to avoid having two sets of macros - one with .byte 0x... and another with string "\x..\x..."

Now it needs duplication.

-Andi

#5 lore raw
In reply to Linus Torvalds
Quoted 1 line · Andi Kleen
Quoted 1 line

Hmm. I thought using the Fibonaci sequence for this was clever :-)

Quoted 4 lines · Linus Torvalds

That's not the fibonacci sequence, that's just a regular sigma(i) (i=1..n) sequence. And if you were to generate the sequence numbers at compile-time I might agree with you, if you also were to avoid using inline asms.

Such things may be checked with:

http://www.research.att.com/~njas/sequences/index.html

-- wli

#6 lore raw
In reply to Linus Torvalds
Quoted 2 lines · Linus Torvalds

That's not the fibonacci sequence, that's just a regular sigma(i) (i=1..n) sequence. And if you were to generate the sequence numbers at compile-time I might agree with you, if you also were to avoid using inline asms.

If the sigma(i) method is to stay, please note that

sigma(1 <= i <= n | i) = n*(n+1) / 2

for all n > 0 (trivial proof by induction).

That is, no need to loop; in this case, with n = num - 1

k = ((num - 1) * num) / 2

Rene.