diff options
author | Gerd Hoffmann <kraxel@suse.de> | 2006-06-26 07:56:16 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-26 13:48:14 -0400 |
commit | d167a51877e94dda73dd656c51f363502309f713 (patch) | |
tree | eb02c2974b61777f575dfdc07d4c2adf83bde434 /arch/x86_64/kernel | |
parent | 240cd6a80642da528bfa382ec2ae4e3cb8991ea7 (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/Makefile | 4 | ||||
-rw-r--r-- | arch/x86_64/kernel/module.c | 38 | ||||
-rw-r--r-- | arch/x86_64/kernel/setup.c | 76 | ||||
-rw-r--r-- | arch/x86_64/kernel/smpboot.c | 4 | ||||
-rw-r--r-- | arch/x86_64/kernel/vmlinux.lds.S | 20 |
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 | ||
13 | obj-$(CONFIG_X86_MCE) += mce.o | 13 | obj-$(CONFIG_X86_MCE) += mce.o |
14 | obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o | 14 | obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o |
@@ -49,3 +49,5 @@ intel_cacheinfo-y += ../../i386/kernel/cpu/intel_cacheinfo.o | |||
49 | quirks-y += ../../i386/kernel/quirks.o | 49 | quirks-y += ../../i386/kernel/quirks.o |
50 | i8237-y += ../../i386/kernel/i8237.o | 50 | i8237-y += ../../i386/kernel/i8237.o |
51 | msr-$(subst m,y,$(CONFIG_X86_MSR)) += ../../i386/kernel/msr.o | 51 | msr-$(subst m,y,$(CONFIG_X86_MSR)) += ../../i386/kernel/msr.o |
52 | alternative-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 | ||
148 | extern void apply_alternatives(void *start, void *end); | ||
149 | |||
150 | int module_finalize(const Elf_Ehdr *hdr, | 148 | int 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 | ||
168 | void module_arch_cleanup(struct module *mod) | 179 | void 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. */ | ||
479 | asm("\t.data\nk8nops: " | ||
480 | K8_NOP1 K8_NOP2 K8_NOP3 K8_NOP4 K8_NOP5 K8_NOP6 | ||
481 | K8_NOP7 K8_NOP8); | ||
482 | |||
483 | extern unsigned char k8nops[]; | ||
484 | static 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 | |||
496 | extern 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". */ | ||
504 | void 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 | |||
532 | static int no_replacement __initdata = 0; | ||
533 | |||
534 | void __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 | |||
542 | static 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) |
551 | struct edd edd; | 477 | struct 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) { |