aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86_64/kernel
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@suse.de>2006-06-26 07:56:16 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-26 13:48:14 -0400
commitd167a51877e94dda73dd656c51f363502309f713 (patch)
treeeb02c2974b61777f575dfdc07d4c2adf83bde434 /arch/x86_64/kernel
parent240cd6a80642da528bfa382ec2ae4e3cb8991ea7 (diff)
[PATCH] x86_64: x86_64 version of the smp alternative patch.
Changes are largely identical to the i386 version: * alternative #define are moved to the new alternative.h file. * one new elf section with pointers to the lock prefixes which can be nop'ed out for non-smp. * two new elf sections simliar to the "classic" alternatives to replace SMP code with simpler UP code. * fixup headers to use alternative.h instead of defining their own LOCK / LOCK_PREFIX macros. The patch reuses the i386 version of the alternatives code to avoid code duplication. The code in alternatives.c was shuffled around a bit to reduce the number of #ifdefs needed. It also got some tweaks needed for x86_64 (vsyscall page handling) and new features (noreplacement option which was x86_64 only up to now). Debug printk's are changed from compile-time to runtime. Loosely based on a early version from Bastian Blank <waldi@debian.org> Signed-off-by: Gerd Hoffmann <kraxel@suse.de> Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/x86_64/kernel')
-rw-r--r--arch/x86_64/kernel/Makefile4
-rw-r--r--arch/x86_64/kernel/module.c38
-rw-r--r--arch/x86_64/kernel/setup.c76
-rw-r--r--arch/x86_64/kernel/smpboot.c4
-rw-r--r--arch/x86_64/kernel/vmlinux.lds.S20
5 files changed, 53 insertions, 89 deletions
diff --git a/arch/x86_64/kernel/Makefile b/arch/x86_64/kernel/Makefile
index 059c88313f4e..381bc6ad743e 100644
--- a/arch/x86_64/kernel/Makefile
+++ b/arch/x86_64/kernel/Makefile
@@ -8,7 +8,7 @@ obj-y := process.o signal.o entry.o traps.o irq.o \
8 ptrace.o time.o ioport.o ldt.o setup.o i8259.o sys_x86_64.o \ 8 ptrace.o time.o ioport.o ldt.o setup.o i8259.o sys_x86_64.o \
9 x8664_ksyms.o i387.o syscall.o vsyscall.o \ 9 x8664_ksyms.o i387.o syscall.o vsyscall.o \
10 setup64.o bootflag.o e820.o reboot.o quirks.o i8237.o \ 10 setup64.o bootflag.o e820.o reboot.o quirks.o i8237.o \
11 pci-dma.o pci-nommu.o 11 pci-dma.o pci-nommu.o alternative.o
12 12
13obj-$(CONFIG_X86_MCE) += mce.o 13obj-$(CONFIG_X86_MCE) += mce.o
14obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o 14obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o
@@ -49,3 +49,5 @@ intel_cacheinfo-y += ../../i386/kernel/cpu/intel_cacheinfo.o
49quirks-y += ../../i386/kernel/quirks.o 49quirks-y += ../../i386/kernel/quirks.o
50i8237-y += ../../i386/kernel/i8237.o 50i8237-y += ../../i386/kernel/i8237.o
51msr-$(subst m,y,$(CONFIG_X86_MSR)) += ../../i386/kernel/msr.o 51msr-$(subst m,y,$(CONFIG_X86_MSR)) += ../../i386/kernel/msr.o
52alternative-y += ../../i386/kernel/alternative.o
53
diff --git a/arch/x86_64/kernel/module.c b/arch/x86_64/kernel/module.c
index bac195c74bcc..9d0958ff547f 100644
--- a/arch/x86_64/kernel/module.c
+++ b/arch/x86_64/kernel/module.c
@@ -145,26 +145,38 @@ int apply_relocate(Elf_Shdr *sechdrs,
145 return -ENOSYS; 145 return -ENOSYS;
146} 146}
147 147
148extern void apply_alternatives(void *start, void *end);
149
150int module_finalize(const Elf_Ehdr *hdr, 148int module_finalize(const Elf_Ehdr *hdr,
151 const Elf_Shdr *sechdrs, 149 const Elf_Shdr *sechdrs,
152 struct module *me) 150 struct module *me)
153{ 151{
154 const Elf_Shdr *s; 152 const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL;
155 char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 153 char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
156 154
157 /* look for .altinstructions to patch */ 155 for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
158 for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { 156 if (!strcmp(".text", secstrings + s->sh_name))
159 void *seg; 157 text = s;
160 if (strcmp(".altinstructions", secstrings + s->sh_name)) 158 if (!strcmp(".altinstructions", secstrings + s->sh_name))
161 continue; 159 alt = s;
162 seg = (void *)s->sh_addr; 160 if (!strcmp(".smp_locks", secstrings + s->sh_name))
163 apply_alternatives(seg, seg + s->sh_size); 161 locks= s;
164 } 162 }
163
164 if (alt) {
165 /* patch .altinstructions */
166 void *aseg = (void *)alt->sh_addr;
167 apply_alternatives(aseg, aseg + alt->sh_size);
168 }
169 if (locks && text) {
170 void *lseg = (void *)locks->sh_addr;
171 void *tseg = (void *)text->sh_addr;
172 alternatives_smp_module_add(me, me->name,
173 lseg, lseg + locks->sh_size,
174 tseg, tseg + text->sh_size);
175 }
165 return 0; 176 return 0;
166} 177}
167 178
168void module_arch_cleanup(struct module *mod) 179void module_arch_cleanup(struct module *mod)
169{ 180{
181 alternatives_smp_module_del(mod);
170} 182}
diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c
index 4b7e02216970..64640c8f5eed 100644
--- a/arch/x86_64/kernel/setup.c
+++ b/arch/x86_64/kernel/setup.c
@@ -473,80 +473,6 @@ contig_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
473} 473}
474#endif 474#endif
475 475
476/* Use inline assembly to define this because the nops are defined
477 as inline assembly strings in the include files and we cannot
478 get them easily into strings. */
479asm("\t.data\nk8nops: "
480 K8_NOP1 K8_NOP2 K8_NOP3 K8_NOP4 K8_NOP5 K8_NOP6
481 K8_NOP7 K8_NOP8);
482
483extern unsigned char k8nops[];
484static unsigned char *k8_nops[ASM_NOP_MAX+1] = {
485 NULL,
486 k8nops,
487 k8nops + 1,
488 k8nops + 1 + 2,
489 k8nops + 1 + 2 + 3,
490 k8nops + 1 + 2 + 3 + 4,
491 k8nops + 1 + 2 + 3 + 4 + 5,
492 k8nops + 1 + 2 + 3 + 4 + 5 + 6,
493 k8nops + 1 + 2 + 3 + 4 + 5 + 6 + 7,
494};
495
496extern char __vsyscall_0;
497
498/* Replace instructions with better alternatives for this CPU type.
499
500 This runs before SMP is initialized to avoid SMP problems with
501 self modifying code. This implies that assymetric systems where
502 APs have less capabilities than the boot processor are not handled.
503 In this case boot with "noreplacement". */
504void apply_alternatives(void *start, void *end)
505{
506 struct alt_instr *a;
507 int diff, i, k;
508 for (a = start; (void *)a < end; a++) {
509 u8 *instr;
510
511 if (!boot_cpu_has(a->cpuid))
512 continue;
513
514 BUG_ON(a->replacementlen > a->instrlen);
515 instr = a->instr;
516 /* vsyscall code is not mapped yet. resolve it manually. */
517 if (instr >= (u8 *)VSYSCALL_START && instr < (u8*)VSYSCALL_END)
518 instr = __va(instr - (u8*)VSYSCALL_START + (u8*)__pa_symbol(&__vsyscall_0));
519 __inline_memcpy(instr, a->replacement, a->replacementlen);
520 diff = a->instrlen - a->replacementlen;
521
522 /* Pad the rest with nops */
523 for (i = a->replacementlen; diff > 0; diff -= k, i += k) {
524 k = diff;
525 if (k > ASM_NOP_MAX)
526 k = ASM_NOP_MAX;
527 __inline_memcpy(instr + i, k8_nops[k], k);
528 }
529 }
530}
531
532static int no_replacement __initdata = 0;
533
534void __init alternative_instructions(void)
535{
536 extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
537 if (no_replacement)
538 return;
539 apply_alternatives(__alt_instructions, __alt_instructions_end);
540}
541
542static int __init noreplacement_setup(char *s)
543{
544 no_replacement = 1;
545 return 1;
546}
547
548__setup("noreplacement", noreplacement_setup);
549
550#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) 476#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
551struct edd edd; 477struct edd edd;
552#ifdef CONFIG_EDD_MODULE 478#ifdef CONFIG_EDD_MODULE
@@ -1303,7 +1229,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
1303 /* Other (Linux-defined) */ 1229 /* Other (Linux-defined) */
1304 "cxmmx", NULL, "cyrix_arr", "centaur_mcr", NULL, 1230 "cxmmx", NULL, "cyrix_arr", "centaur_mcr", NULL,
1305 "constant_tsc", NULL, NULL, 1231 "constant_tsc", NULL, NULL,
1306 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1232 "up", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1307 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1233 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1308 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1234 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1309 1235
diff --git a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c
index 71a7222cf9ce..06535e7687ce 100644
--- a/arch/x86_64/kernel/smpboot.c
+++ b/arch/x86_64/kernel/smpboot.c
@@ -797,6 +797,8 @@ static int __cpuinit do_boot_cpu(int cpu, int apicid)
797 } 797 }
798 798
799 799
800 alternatives_smp_switch(1);
801
800 c_idle.idle = get_idle_for_cpu(cpu); 802 c_idle.idle = get_idle_for_cpu(cpu);
801 803
802 if (c_idle.idle) { 804 if (c_idle.idle) {
@@ -1259,6 +1261,8 @@ void __cpu_die(unsigned int cpu)
1259 /* They ack this in play_dead by setting CPU_DEAD */ 1261 /* They ack this in play_dead by setting CPU_DEAD */
1260 if (per_cpu(cpu_state, cpu) == CPU_DEAD) { 1262 if (per_cpu(cpu_state, cpu) == CPU_DEAD) {
1261 printk ("CPU %d is now offline\n", cpu); 1263 printk ("CPU %d is now offline\n", cpu);
1264 if (1 == num_online_cpus())
1265 alternatives_smp_switch(0);
1262 return; 1266 return;
1263 } 1267 }
1264 msleep(100); 1268 msleep(100);
diff --git a/arch/x86_64/kernel/vmlinux.lds.S b/arch/x86_64/kernel/vmlinux.lds.S
index b81f473c4a19..5968c2415da9 100644
--- a/arch/x86_64/kernel/vmlinux.lds.S
+++ b/arch/x86_64/kernel/vmlinux.lds.S
@@ -131,6 +131,26 @@ SECTIONS
131 *(.data.page_aligned) 131 *(.data.page_aligned)
132 } 132 }
133 133
134 /* might get freed after init */
135 . = ALIGN(4096);
136 __smp_alt_begin = .;
137 __smp_alt_instructions = .;
138 .smp_altinstructions : AT(ADDR(.smp_altinstructions) - LOAD_OFFSET) {
139 *(.smp_altinstructions)
140 }
141 __smp_alt_instructions_end = .;
142 . = ALIGN(8);
143 __smp_locks = .;
144 .smp_locks : AT(ADDR(.smp_locks) - LOAD_OFFSET) {
145 *(.smp_locks)
146 }
147 __smp_locks_end = .;
148 .smp_altinstr_replacement : AT(ADDR(.smp_altinstr_replacement) - LOAD_OFFSET) {
149 *(.smp_altinstr_replacement)
150 }
151 . = ALIGN(4096);
152 __smp_alt_end = .;
153
134 . = ALIGN(4096); /* Init code and data */ 154 . = ALIGN(4096); /* Init code and data */
135 __init_begin = .; 155 __init_begin = .;
136 .init.text : AT(ADDR(.init.text) - LOAD_OFFSET) { 156 .init.text : AT(ADDR(.init.text) - LOAD_OFFSET) {