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 | |
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')
-rw-r--r-- | arch/i386/kernel/alternative.c | 118 | ||||
-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 | ||||
-rw-r--r-- | arch/x86_64/mm/init.c | 27 |
7 files changed, 152 insertions, 135 deletions
diff --git a/arch/i386/kernel/alternative.c b/arch/i386/kernel/alternative.c index 5cbd6f99fb2a..50eb0e03777e 100644 --- a/arch/i386/kernel/alternative.c +++ b/arch/i386/kernel/alternative.c | |||
@@ -4,27 +4,41 @@ | |||
4 | #include <asm/alternative.h> | 4 | #include <asm/alternative.h> |
5 | #include <asm/sections.h> | 5 | #include <asm/sections.h> |
6 | 6 | ||
7 | #define DEBUG 0 | 7 | static int no_replacement = 0; |
8 | #if DEBUG | 8 | static int smp_alt_once = 0; |
9 | # define DPRINTK(fmt, args...) printk(fmt, args) | 9 | static int debug_alternative = 0; |
10 | #else | 10 | |
11 | # define DPRINTK(fmt, args...) | 11 | static int __init noreplacement_setup(char *s) |
12 | #endif | 12 | { |
13 | no_replacement = 1; | ||
14 | return 1; | ||
15 | } | ||
16 | static int __init bootonly(char *str) | ||
17 | { | ||
18 | smp_alt_once = 1; | ||
19 | return 1; | ||
20 | } | ||
21 | static int __init debug_alt(char *str) | ||
22 | { | ||
23 | debug_alternative = 1; | ||
24 | return 1; | ||
25 | } | ||
13 | 26 | ||
27 | __setup("noreplacement", noreplacement_setup); | ||
28 | __setup("smp-alt-boot", bootonly); | ||
29 | __setup("debug-alternative", debug_alt); | ||
30 | |||
31 | #define DPRINTK(fmt, args...) if (debug_alternative) \ | ||
32 | printk(KERN_DEBUG fmt, args) | ||
33 | |||
34 | #ifdef GENERIC_NOP1 | ||
14 | /* Use inline assembly to define this because the nops are defined | 35 | /* Use inline assembly to define this because the nops are defined |
15 | as inline assembly strings in the include files and we cannot | 36 | as inline assembly strings in the include files and we cannot |
16 | get them easily into strings. */ | 37 | get them easily into strings. */ |
17 | asm("\t.data\nintelnops: " | 38 | asm("\t.data\nintelnops: " |
18 | GENERIC_NOP1 GENERIC_NOP2 GENERIC_NOP3 GENERIC_NOP4 GENERIC_NOP5 GENERIC_NOP6 | 39 | GENERIC_NOP1 GENERIC_NOP2 GENERIC_NOP3 GENERIC_NOP4 GENERIC_NOP5 GENERIC_NOP6 |
19 | GENERIC_NOP7 GENERIC_NOP8); | 40 | GENERIC_NOP7 GENERIC_NOP8); |
20 | asm("\t.data\nk8nops: " | 41 | extern unsigned char intelnops[]; |
21 | K8_NOP1 K8_NOP2 K8_NOP3 K8_NOP4 K8_NOP5 K8_NOP6 | ||
22 | K8_NOP7 K8_NOP8); | ||
23 | asm("\t.data\nk7nops: " | ||
24 | K7_NOP1 K7_NOP2 K7_NOP3 K7_NOP4 K7_NOP5 K7_NOP6 | ||
25 | K7_NOP7 K7_NOP8); | ||
26 | |||
27 | extern unsigned char intelnops[], k8nops[], k7nops[]; | ||
28 | static unsigned char *intel_nops[ASM_NOP_MAX+1] = { | 42 | static unsigned char *intel_nops[ASM_NOP_MAX+1] = { |
29 | NULL, | 43 | NULL, |
30 | intelnops, | 44 | intelnops, |
@@ -36,6 +50,13 @@ static unsigned char *intel_nops[ASM_NOP_MAX+1] = { | |||
36 | intelnops + 1 + 2 + 3 + 4 + 5 + 6, | 50 | intelnops + 1 + 2 + 3 + 4 + 5 + 6, |
37 | intelnops + 1 + 2 + 3 + 4 + 5 + 6 + 7, | 51 | intelnops + 1 + 2 + 3 + 4 + 5 + 6 + 7, |
38 | }; | 52 | }; |
53 | #endif | ||
54 | |||
55 | #ifdef K8_NOP1 | ||
56 | asm("\t.data\nk8nops: " | ||
57 | K8_NOP1 K8_NOP2 K8_NOP3 K8_NOP4 K8_NOP5 K8_NOP6 | ||
58 | K8_NOP7 K8_NOP8); | ||
59 | extern unsigned char k8nops[]; | ||
39 | static unsigned char *k8_nops[ASM_NOP_MAX+1] = { | 60 | static unsigned char *k8_nops[ASM_NOP_MAX+1] = { |
40 | NULL, | 61 | NULL, |
41 | k8nops, | 62 | k8nops, |
@@ -47,6 +68,13 @@ static unsigned char *k8_nops[ASM_NOP_MAX+1] = { | |||
47 | k8nops + 1 + 2 + 3 + 4 + 5 + 6, | 68 | k8nops + 1 + 2 + 3 + 4 + 5 + 6, |
48 | k8nops + 1 + 2 + 3 + 4 + 5 + 6 + 7, | 69 | k8nops + 1 + 2 + 3 + 4 + 5 + 6 + 7, |
49 | }; | 70 | }; |
71 | #endif | ||
72 | |||
73 | #ifdef K7_NOP1 | ||
74 | asm("\t.data\nk7nops: " | ||
75 | K7_NOP1 K7_NOP2 K7_NOP3 K7_NOP4 K7_NOP5 K7_NOP6 | ||
76 | K7_NOP7 K7_NOP8); | ||
77 | extern unsigned char k7nops[]; | ||
50 | static unsigned char *k7_nops[ASM_NOP_MAX+1] = { | 78 | static unsigned char *k7_nops[ASM_NOP_MAX+1] = { |
51 | NULL, | 79 | NULL, |
52 | k7nops, | 80 | k7nops, |
@@ -58,6 +86,18 @@ static unsigned char *k7_nops[ASM_NOP_MAX+1] = { | |||
58 | k7nops + 1 + 2 + 3 + 4 + 5 + 6, | 86 | k7nops + 1 + 2 + 3 + 4 + 5 + 6, |
59 | k7nops + 1 + 2 + 3 + 4 + 5 + 6 + 7, | 87 | k7nops + 1 + 2 + 3 + 4 + 5 + 6 + 7, |
60 | }; | 88 | }; |
89 | #endif | ||
90 | |||
91 | #ifdef CONFIG_X86_64 | ||
92 | |||
93 | extern char __vsyscall_0; | ||
94 | static inline unsigned char** find_nop_table(void) | ||
95 | { | ||
96 | return k8_nops; | ||
97 | } | ||
98 | |||
99 | #else /* CONFIG_X86_64 */ | ||
100 | |||
61 | static struct nop { | 101 | static struct nop { |
62 | int cpuid; | 102 | int cpuid; |
63 | unsigned char **noptable; | 103 | unsigned char **noptable; |
@@ -67,14 +107,6 @@ static struct nop { | |||
67 | { -1, NULL } | 107 | { -1, NULL } |
68 | }; | 108 | }; |
69 | 109 | ||
70 | |||
71 | extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; | ||
72 | extern struct alt_instr __smp_alt_instructions[], __smp_alt_instructions_end[]; | ||
73 | extern u8 *__smp_locks[], *__smp_locks_end[]; | ||
74 | |||
75 | extern u8 __smp_alt_begin[], __smp_alt_end[]; | ||
76 | |||
77 | |||
78 | static unsigned char** find_nop_table(void) | 110 | static unsigned char** find_nop_table(void) |
79 | { | 111 | { |
80 | unsigned char **noptable = intel_nops; | 112 | unsigned char **noptable = intel_nops; |
@@ -89,6 +121,14 @@ static unsigned char** find_nop_table(void) | |||
89 | return noptable; | 121 | return noptable; |
90 | } | 122 | } |
91 | 123 | ||
124 | #endif /* CONFIG_X86_64 */ | ||
125 | |||
126 | extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; | ||
127 | extern struct alt_instr __smp_alt_instructions[], __smp_alt_instructions_end[]; | ||
128 | extern u8 *__smp_locks[], *__smp_locks_end[]; | ||
129 | |||
130 | extern u8 __smp_alt_begin[], __smp_alt_end[]; | ||
131 | |||
92 | /* Replace instructions with better alternatives for this CPU type. | 132 | /* Replace instructions with better alternatives for this CPU type. |
93 | This runs before SMP is initialized to avoid SMP problems with | 133 | This runs before SMP is initialized to avoid SMP problems with |
94 | self modifying code. This implies that assymetric systems where | 134 | self modifying code. This implies that assymetric systems where |
@@ -99,6 +139,7 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end) | |||
99 | { | 139 | { |
100 | unsigned char **noptable = find_nop_table(); | 140 | unsigned char **noptable = find_nop_table(); |
101 | struct alt_instr *a; | 141 | struct alt_instr *a; |
142 | u8 *instr; | ||
102 | int diff, i, k; | 143 | int diff, i, k; |
103 | 144 | ||
104 | DPRINTK("%s: alt table %p -> %p\n", __FUNCTION__, start, end); | 145 | DPRINTK("%s: alt table %p -> %p\n", __FUNCTION__, start, end); |
@@ -106,7 +147,16 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end) | |||
106 | BUG_ON(a->replacementlen > a->instrlen); | 147 | BUG_ON(a->replacementlen > a->instrlen); |
107 | if (!boot_cpu_has(a->cpuid)) | 148 | if (!boot_cpu_has(a->cpuid)) |
108 | continue; | 149 | continue; |
109 | memcpy(a->instr, a->replacement, a->replacementlen); | 150 | instr = a->instr; |
151 | #ifdef CONFIG_X86_64 | ||
152 | /* vsyscall code is not mapped yet. resolve it manually. */ | ||
153 | if (instr >= (u8 *)VSYSCALL_START && instr < (u8*)VSYSCALL_END) { | ||
154 | instr = __va(instr - (u8*)VSYSCALL_START + (u8*)__pa_symbol(&__vsyscall_0)); | ||
155 | DPRINTK("%s: vsyscall fixup: %p => %p\n", | ||
156 | __FUNCTION__, a->instr, instr); | ||
157 | } | ||
158 | #endif | ||
159 | memcpy(instr, a->replacement, a->replacementlen); | ||
110 | diff = a->instrlen - a->replacementlen; | 160 | diff = a->instrlen - a->replacementlen; |
111 | /* Pad the rest with nops */ | 161 | /* Pad the rest with nops */ |
112 | for (i = a->replacementlen; diff > 0; diff -= k, i += k) { | 162 | for (i = a->replacementlen; diff > 0; diff -= k, i += k) { |
@@ -186,14 +236,6 @@ struct smp_alt_module { | |||
186 | static LIST_HEAD(smp_alt_modules); | 236 | static LIST_HEAD(smp_alt_modules); |
187 | static DEFINE_SPINLOCK(smp_alt); | 237 | static DEFINE_SPINLOCK(smp_alt); |
188 | 238 | ||
189 | static int smp_alt_once = 0; | ||
190 | static int __init bootonly(char *str) | ||
191 | { | ||
192 | smp_alt_once = 1; | ||
193 | return 1; | ||
194 | } | ||
195 | __setup("smp-alt-boot", bootonly); | ||
196 | |||
197 | void alternatives_smp_module_add(struct module *mod, char *name, | 239 | void alternatives_smp_module_add(struct module *mod, char *name, |
198 | void *locks, void *locks_end, | 240 | void *locks, void *locks_end, |
199 | void *text, void *text_end) | 241 | void *text, void *text_end) |
@@ -201,6 +243,9 @@ void alternatives_smp_module_add(struct module *mod, char *name, | |||
201 | struct smp_alt_module *smp; | 243 | struct smp_alt_module *smp; |
202 | unsigned long flags; | 244 | unsigned long flags; |
203 | 245 | ||
246 | if (no_replacement) | ||
247 | return; | ||
248 | |||
204 | if (smp_alt_once) { | 249 | if (smp_alt_once) { |
205 | if (boot_cpu_has(X86_FEATURE_UP)) | 250 | if (boot_cpu_has(X86_FEATURE_UP)) |
206 | alternatives_smp_unlock(locks, locks_end, | 251 | alternatives_smp_unlock(locks, locks_end, |
@@ -235,7 +280,7 @@ void alternatives_smp_module_del(struct module *mod) | |||
235 | struct smp_alt_module *item; | 280 | struct smp_alt_module *item; |
236 | unsigned long flags; | 281 | unsigned long flags; |
237 | 282 | ||
238 | if (smp_alt_once) | 283 | if (no_replacement || smp_alt_once) |
239 | return; | 284 | return; |
240 | 285 | ||
241 | spin_lock_irqsave(&smp_alt, flags); | 286 | spin_lock_irqsave(&smp_alt, flags); |
@@ -256,7 +301,7 @@ void alternatives_smp_switch(int smp) | |||
256 | struct smp_alt_module *mod; | 301 | struct smp_alt_module *mod; |
257 | unsigned long flags; | 302 | unsigned long flags; |
258 | 303 | ||
259 | if (smp_alt_once) | 304 | if (no_replacement || smp_alt_once) |
260 | return; | 305 | return; |
261 | BUG_ON(!smp && (num_online_cpus() > 1)); | 306 | BUG_ON(!smp && (num_online_cpus() > 1)); |
262 | 307 | ||
@@ -285,6 +330,13 @@ void alternatives_smp_switch(int smp) | |||
285 | 330 | ||
286 | void __init alternative_instructions(void) | 331 | void __init alternative_instructions(void) |
287 | { | 332 | { |
333 | if (no_replacement) { | ||
334 | printk(KERN_INFO "(SMP-)alternatives turned off\n"); | ||
335 | free_init_pages("SMP alternatives", | ||
336 | (unsigned long)__smp_alt_begin, | ||
337 | (unsigned long)__smp_alt_end); | ||
338 | return; | ||
339 | } | ||
288 | apply_alternatives(__alt_instructions, __alt_instructions_end); | 340 | apply_alternatives(__alt_instructions, __alt_instructions_end); |
289 | 341 | ||
290 | /* switch to patch-once-at-boottime-only mode and free the | 342 | /* switch to patch-once-at-boottime-only mode and free the |
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) { |
diff --git a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c index 4ba34e95d835..a70a8e3e312d 100644 --- a/arch/x86_64/mm/init.c +++ b/arch/x86_64/mm/init.c | |||
@@ -644,20 +644,29 @@ void __init mem_init(void) | |||
644 | #endif | 644 | #endif |
645 | } | 645 | } |
646 | 646 | ||
647 | void free_initmem(void) | 647 | void free_init_pages(char *what, unsigned long begin, unsigned long end) |
648 | { | 648 | { |
649 | unsigned long addr; | 649 | unsigned long addr; |
650 | 650 | ||
651 | addr = (unsigned long)(&__init_begin); | 651 | if (begin >= end) |
652 | for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { | 652 | return; |
653 | |||
654 | printk(KERN_INFO "Freeing %s: %ldk freed\n", what, (end - begin) >> 10); | ||
655 | for (addr = begin; addr < end; addr += PAGE_SIZE) { | ||
653 | ClearPageReserved(virt_to_page(addr)); | 656 | ClearPageReserved(virt_to_page(addr)); |
654 | init_page_count(virt_to_page(addr)); | 657 | init_page_count(virt_to_page(addr)); |
655 | memset((void *)(addr & ~(PAGE_SIZE-1)), 0xcc, PAGE_SIZE); | 658 | memset((void *)(addr & ~(PAGE_SIZE-1)), 0xcc, PAGE_SIZE); |
656 | free_page(addr); | 659 | free_page(addr); |
657 | totalram_pages++; | 660 | totalram_pages++; |
658 | } | 661 | } |
662 | } | ||
663 | |||
664 | void free_initmem(void) | ||
665 | { | ||
659 | memset(__initdata_begin, 0xba, __initdata_end - __initdata_begin); | 666 | memset(__initdata_begin, 0xba, __initdata_end - __initdata_begin); |
660 | printk ("Freeing unused kernel memory: %luk freed\n", (__init_end - __init_begin) >> 10); | 667 | free_init_pages("unused kernel memory", |
668 | (unsigned long)(&__init_begin), | ||
669 | (unsigned long)(&__init_end)); | ||
661 | } | 670 | } |
662 | 671 | ||
663 | #ifdef CONFIG_DEBUG_RODATA | 672 | #ifdef CONFIG_DEBUG_RODATA |
@@ -686,15 +695,7 @@ void mark_rodata_ro(void) | |||
686 | #ifdef CONFIG_BLK_DEV_INITRD | 695 | #ifdef CONFIG_BLK_DEV_INITRD |
687 | void free_initrd_mem(unsigned long start, unsigned long end) | 696 | void free_initrd_mem(unsigned long start, unsigned long end) |
688 | { | 697 | { |
689 | if (start >= end) | 698 | free_init_pages("initrd memory", start, end); |
690 | return; | ||
691 | printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); | ||
692 | for (; start < end; start += PAGE_SIZE) { | ||
693 | ClearPageReserved(virt_to_page(start)); | ||
694 | init_page_count(virt_to_page(start)); | ||
695 | free_page(start); | ||
696 | totalram_pages++; | ||
697 | } | ||
698 | } | 699 | } |
699 | #endif | 700 | #endif |
700 | 701 | ||