diff --git a/debian/changelog b/debian/changelog index 615e05283..2c12767b6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,7 +3,10 @@ linux-2.6 (2.6.17-5) UNRELEASED; urgency=low [ Martin Michlmayr ] * [arm/nslu2] Enable CONFIG_USB_EHCI_SPLIT_ISO. Closes: #378554 - -- Martin Michlmayr Mon, 17 Jul 2006 16:21:05 +0200 + [ Frederik Schüler ] + * [amd64] Apply rediffed smp-alternatives patch from 2.6.18-rc1. + + -- Frederik Schüler Sat, 22 Jul 2006 01:29:08 +0200 linux-2.6 (2.6.17-4) unstable; urgency=low diff --git a/debian/patches/series/5 b/debian/patches/series/5 new file mode 100644 index 000000000..d89a2aba9 --- /dev/null +++ b/debian/patches/series/5 @@ -0,0 +1 @@ ++ x86_64-smp-alternatives.patch diff --git a/debian/patches/x86_64-smp-alternatives.patch b/debian/patches/x86_64-smp-alternatives.patch new file mode 100644 index 000000000..7597374b3 --- /dev/null +++ b/debian/patches/x86_64-smp-alternatives.patch @@ -0,0 +1,1144 @@ +From: Gerd Hoffmann +Date: Mon, 26 Jun 2006 11:56:16 +0000 (+0200) +Subject: [PATCH] x86_64: x86_64 version of the smp alternative patch. +X-Git-Tag: v2.6.18-rc1 +X-Git-Url: http://www.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=d167a51877e94dda73dd656c51f363502309f713 + +[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 + +Signed-off-by: Gerd Hoffmann +Signed-off-by: Andi Kleen +Signed-off-by: Linus Torvalds +--- +diff -pruN linux-2.6.17-a/arch/i386/kernel/alternative.c linux-2.6.17-b/arch/i386/kernel/alternative.c +--- linux-2.6.17-a/arch/i386/kernel/alternative.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/arch/i386/kernel/alternative.c 2006-07-22 01:11:40.000000000 +0200 +@@ -4,27 +4,41 @@ + #include + #include + +-#define DEBUG 0 +-#if DEBUG +-# define DPRINTK(fmt, args...) printk(fmt, args) +-#else +-# define DPRINTK(fmt, args...) +-#endif ++static int no_replacement = 0; ++static int smp_alt_once = 0; ++static int debug_alternative = 0; ++ ++static int __init noreplacement_setup(char *s) ++{ ++ no_replacement = 1; ++ return 1; ++} ++static int __init bootonly(char *str) ++{ ++ smp_alt_once = 1; ++ return 1; ++} ++static int __init debug_alt(char *str) ++{ ++ debug_alternative = 1; ++ return 1; ++} + ++__setup("noreplacement", noreplacement_setup); ++__setup("smp-alt-boot", bootonly); ++__setup("debug-alternative", debug_alt); ++ ++#define DPRINTK(fmt, args...) if (debug_alternative) \ ++ printk(KERN_DEBUG fmt, args) ++ ++#ifdef GENERIC_NOP1 + /* Use inline assembly to define this because the nops are defined + as inline assembly strings in the include files and we cannot + get them easily into strings. */ + asm("\t.data\nintelnops: " + GENERIC_NOP1 GENERIC_NOP2 GENERIC_NOP3 GENERIC_NOP4 GENERIC_NOP5 GENERIC_NOP6 + GENERIC_NOP7 GENERIC_NOP8); +-asm("\t.data\nk8nops: " +- K8_NOP1 K8_NOP2 K8_NOP3 K8_NOP4 K8_NOP5 K8_NOP6 +- K8_NOP7 K8_NOP8); +-asm("\t.data\nk7nops: " +- K7_NOP1 K7_NOP2 K7_NOP3 K7_NOP4 K7_NOP5 K7_NOP6 +- K7_NOP7 K7_NOP8); +- +-extern unsigned char intelnops[], k8nops[], k7nops[]; ++extern unsigned char intelnops[]; + static unsigned char *intel_nops[ASM_NOP_MAX+1] = { + NULL, + intelnops, +@@ -36,6 +50,13 @@ static unsigned char *intel_nops[ASM_NOP + intelnops + 1 + 2 + 3 + 4 + 5 + 6, + intelnops + 1 + 2 + 3 + 4 + 5 + 6 + 7, + }; ++#endif ++ ++#ifdef K8_NOP1 ++asm("\t.data\nk8nops: " ++ K8_NOP1 K8_NOP2 K8_NOP3 K8_NOP4 K8_NOP5 K8_NOP6 ++ K8_NOP7 K8_NOP8); ++extern unsigned char k8nops[]; + static unsigned char *k8_nops[ASM_NOP_MAX+1] = { + NULL, + k8nops, +@@ -47,6 +68,13 @@ static unsigned char *k8_nops[ASM_NOP_MA + k8nops + 1 + 2 + 3 + 4 + 5 + 6, + k8nops + 1 + 2 + 3 + 4 + 5 + 6 + 7, + }; ++#endif ++ ++#ifdef K7_NOP1 ++asm("\t.data\nk7nops: " ++ K7_NOP1 K7_NOP2 K7_NOP3 K7_NOP4 K7_NOP5 K7_NOP6 ++ K7_NOP7 K7_NOP8); ++extern unsigned char k7nops[]; + static unsigned char *k7_nops[ASM_NOP_MAX+1] = { + NULL, + k7nops, +@@ -58,6 +86,18 @@ static unsigned char *k7_nops[ASM_NOP_MA + k7nops + 1 + 2 + 3 + 4 + 5 + 6, + k7nops + 1 + 2 + 3 + 4 + 5 + 6 + 7, + }; ++#endif ++ ++#ifdef CONFIG_X86_64 ++ ++extern char __vsyscall_0; ++static inline unsigned char** find_nop_table(void) ++{ ++ return k8_nops; ++} ++ ++#else /* CONFIG_X86_64 */ ++ + static struct nop { + int cpuid; + unsigned char **noptable; +@@ -67,14 +107,6 @@ static struct nop { + { -1, NULL } + }; + +- +-extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; +-extern struct alt_instr __smp_alt_instructions[], __smp_alt_instructions_end[]; +-extern u8 *__smp_locks[], *__smp_locks_end[]; +- +-extern u8 __smp_alt_begin[], __smp_alt_end[]; +- +- + static unsigned char** find_nop_table(void) + { + unsigned char **noptable = intel_nops; +@@ -89,6 +121,14 @@ static unsigned char** find_nop_table(vo + return noptable; + } + ++#endif /* CONFIG_X86_64 */ ++ ++extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; ++extern struct alt_instr __smp_alt_instructions[], __smp_alt_instructions_end[]; ++extern u8 *__smp_locks[], *__smp_locks_end[]; ++ ++extern u8 __smp_alt_begin[], __smp_alt_end[]; ++ + /* 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 +@@ -99,6 +139,7 @@ void apply_alternatives(struct alt_instr + { + unsigned char **noptable = find_nop_table(); + struct alt_instr *a; ++ u8 *instr; + int diff, i, k; + + DPRINTK("%s: alt table %p -> %p\n", __FUNCTION__, start, end); +@@ -106,7 +147,16 @@ void apply_alternatives(struct alt_instr + BUG_ON(a->replacementlen > a->instrlen); + if (!boot_cpu_has(a->cpuid)) + continue; +- memcpy(a->instr, a->replacement, a->replacementlen); ++ instr = a->instr; ++#ifdef CONFIG_X86_64 ++ /* vsyscall code is not mapped yet. resolve it manually. */ ++ if (instr >= (u8 *)VSYSCALL_START && instr < (u8*)VSYSCALL_END) { ++ instr = __va(instr - (u8*)VSYSCALL_START + (u8*)__pa_symbol(&__vsyscall_0)); ++ DPRINTK("%s: vsyscall fixup: %p => %p\n", ++ __FUNCTION__, a->instr, instr); ++ } ++#endif ++ memcpy(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) { +@@ -186,14 +236,6 @@ struct smp_alt_module { + static LIST_HEAD(smp_alt_modules); + static DEFINE_SPINLOCK(smp_alt); + +-static int smp_alt_once = 0; +-static int __init bootonly(char *str) +-{ +- smp_alt_once = 1; +- return 1; +-} +-__setup("smp-alt-boot", bootonly); +- + void alternatives_smp_module_add(struct module *mod, char *name, + void *locks, void *locks_end, + void *text, void *text_end) +@@ -201,6 +243,9 @@ void alternatives_smp_module_add(struct + struct smp_alt_module *smp; + unsigned long flags; + ++ if (no_replacement) ++ return; ++ + if (smp_alt_once) { + if (boot_cpu_has(X86_FEATURE_UP)) + alternatives_smp_unlock(locks, locks_end, +@@ -235,7 +280,7 @@ void alternatives_smp_module_del(struct + struct smp_alt_module *item; + unsigned long flags; + +- if (smp_alt_once) ++ if (no_replacement || smp_alt_once) + return; + + spin_lock_irqsave(&smp_alt, flags); +@@ -256,7 +301,7 @@ void alternatives_smp_switch(int smp) + struct smp_alt_module *mod; + unsigned long flags; + +- if (smp_alt_once) ++ if (no_replacement || smp_alt_once) + return; + BUG_ON(!smp && (num_online_cpus() > 1)); + +@@ -285,6 +330,13 @@ void alternatives_smp_switch(int smp) + + void __init alternative_instructions(void) + { ++ if (no_replacement) { ++ printk(KERN_INFO "(SMP-)alternatives turned off\n"); ++ free_init_pages("SMP alternatives", ++ (unsigned long)__smp_alt_begin, ++ (unsigned long)__smp_alt_end); ++ return; ++ } + apply_alternatives(__alt_instructions, __alt_instructions_end); + + /* switch to patch-once-at-boottime-only mode and free the +diff -pruN linux-2.6.17-a/arch/x86_64/kernel/Makefile linux-2.6.17-b/arch/x86_64/kernel/Makefile +--- linux-2.6.17-a/arch/x86_64/kernel/Makefile 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/arch/x86_64/kernel/Makefile 2006-07-22 01:11:40.000000000 +0200 +@@ -8,7 +8,7 @@ obj-y := process.o signal.o entry.o trap + ptrace.o time.o ioport.o ldt.o setup.o i8259.o sys_x86_64.o \ + x8664_ksyms.o i387.o syscall.o vsyscall.o \ + setup64.o bootflag.o e820.o reboot.o quirks.o i8237.o \ +- pci-dma.o pci-nommu.o ++ pci-dma.o pci-nommu.o alternative.o + + obj-$(CONFIG_X86_MCE) += mce.o + obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o +@@ -49,3 +49,5 @@ intel_cacheinfo-y += ../../i386/kernel/ + quirks-y += ../../i386/kernel/quirks.o + i8237-y += ../../i386/kernel/i8237.o + msr-$(subst m,y,$(CONFIG_X86_MSR)) += ../../i386/kernel/msr.o ++alternative-y += ../../i386/kernel/alternative.o ++ +diff -pruN linux-2.6.17-a/arch/x86_64/kernel/module.c linux-2.6.17-b/arch/x86_64/kernel/module.c +--- linux-2.6.17-a/arch/x86_64/kernel/module.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/arch/x86_64/kernel/module.c 2006-07-22 01:11:40.000000000 +0200 +@@ -145,26 +145,38 @@ int apply_relocate(Elf_Shdr *sechdrs, + return -ENOSYS; + } + +-extern void apply_alternatives(void *start, void *end); +- + int module_finalize(const Elf_Ehdr *hdr, +- const Elf_Shdr *sechdrs, +- struct module *me) ++ const Elf_Shdr *sechdrs, ++ struct module *me) + { +- const Elf_Shdr *s; ++ const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL; + char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + +- /* look for .altinstructions to patch */ +- for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { +- void *seg; +- if (strcmp(".altinstructions", secstrings + s->sh_name)) +- continue; +- seg = (void *)s->sh_addr; +- apply_alternatives(seg, seg + s->sh_size); +- } ++ for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { ++ if (!strcmp(".text", secstrings + s->sh_name)) ++ text = s; ++ if (!strcmp(".altinstructions", secstrings + s->sh_name)) ++ alt = s; ++ if (!strcmp(".smp_locks", secstrings + s->sh_name)) ++ locks= s; ++ } ++ ++ if (alt) { ++ /* patch .altinstructions */ ++ void *aseg = (void *)alt->sh_addr; ++ apply_alternatives(aseg, aseg + alt->sh_size); ++ } ++ if (locks && text) { ++ void *lseg = (void *)locks->sh_addr; ++ void *tseg = (void *)text->sh_addr; ++ alternatives_smp_module_add(me, me->name, ++ lseg, lseg + locks->sh_size, ++ tseg, tseg + text->sh_size); ++ } + return 0; + } + + void module_arch_cleanup(struct module *mod) + { ++ alternatives_smp_module_del(mod); + } +diff -pruN linux-2.6.17-a/arch/x86_64/kernel/setup.c linux-2.6.17-b/arch/x86_64/kernel/setup.c +--- linux-2.6.17-a/arch/x86_64/kernel/setup.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/arch/x86_64/kernel/setup.c 2006-07-22 01:11:40.000000000 +0200 +@@ -473,80 +473,6 @@ contig_initmem_init(unsigned long start_ + } + #endif + +-/* Use inline assembly to define this because the nops are defined +- as inline assembly strings in the include files and we cannot +- get them easily into strings. */ +-asm("\t.data\nk8nops: " +- K8_NOP1 K8_NOP2 K8_NOP3 K8_NOP4 K8_NOP5 K8_NOP6 +- K8_NOP7 K8_NOP8); +- +-extern unsigned char k8nops[]; +-static unsigned char *k8_nops[ASM_NOP_MAX+1] = { +- NULL, +- k8nops, +- k8nops + 1, +- k8nops + 1 + 2, +- k8nops + 1 + 2 + 3, +- k8nops + 1 + 2 + 3 + 4, +- k8nops + 1 + 2 + 3 + 4 + 5, +- k8nops + 1 + 2 + 3 + 4 + 5 + 6, +- k8nops + 1 + 2 + 3 + 4 + 5 + 6 + 7, +-}; +- +-extern char __vsyscall_0; +- +-/* 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; (void *)a < end; a++) { +- u8 *instr; +- +- if (!boot_cpu_has(a->cpuid)) +- continue; +- +- BUG_ON(a->replacementlen > a->instrlen); +- instr = a->instr; +- /* vsyscall code is not mapped yet. resolve it manually. */ +- if (instr >= (u8 *)VSYSCALL_START && instr < (u8*)VSYSCALL_END) +- instr = __va(instr - (u8*)VSYSCALL_START + (u8*)__pa_symbol(&__vsyscall_0)); +- __inline_memcpy(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) { +- k = diff; +- if (k > ASM_NOP_MAX) +- k = ASM_NOP_MAX; +- __inline_memcpy(instr + i, k8_nops[k], k); +- } +- } +-} +- +-static int no_replacement __initdata = 0; +- +-void __init alternative_instructions(void) +-{ +- extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; +- if (no_replacement) +- return; +- apply_alternatives(__alt_instructions, __alt_instructions_end); +-} +- +-static int __init noreplacement_setup(char *s) +-{ +- no_replacement = 1; +- return 1; +-} +- +-__setup("noreplacement", noreplacement_setup); +- + #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) + struct edd edd; + #ifdef CONFIG_EDD_MODULE +@@ -1294,7 +1220,7 @@ static int show_cpuinfo(struct seq_file + /* Other (Linux-defined) */ + "cxmmx", NULL, "cyrix_arr", "centaur_mcr", NULL, + "constant_tsc", NULL, NULL, +- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ++ "up", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + +diff -pruN linux-2.6.17-a/arch/x86_64/kernel/smpboot.c linux-2.6.17-b/arch/x86_64/kernel/smpboot.c +--- linux-2.6.17-a/arch/x86_64/kernel/smpboot.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/arch/x86_64/kernel/smpboot.c 2006-07-22 01:11:40.000000000 +0200 +@@ -797,6 +797,8 @@ static int __cpuinit do_boot_cpu(int cpu + } + + ++ alternatives_smp_switch(1); ++ + c_idle.idle = get_idle_for_cpu(cpu); + + if (c_idle.idle) { +@@ -1259,6 +1261,8 @@ void __cpu_die(unsigned int cpu) + /* They ack this in play_dead by setting CPU_DEAD */ + if (per_cpu(cpu_state, cpu) == CPU_DEAD) { + printk ("CPU %d is now offline\n", cpu); ++ if (1 == num_online_cpus()) ++ alternatives_smp_switch(0); + return; + } + msleep(100); +diff -pruN linux-2.6.17-a/arch/x86_64/kernel/vmlinux.lds.S linux-2.6.17-b/arch/x86_64/kernel/vmlinux.lds.S +--- linux-2.6.17-a/arch/x86_64/kernel/vmlinux.lds.S 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/arch/x86_64/kernel/vmlinux.lds.S 2006-07-22 01:11:40.000000000 +0200 +@@ -131,6 +131,26 @@ SECTIONS + *(.data.page_aligned) + } + ++ /* might get freed after init */ ++ . = ALIGN(4096); ++ __smp_alt_begin = .; ++ __smp_alt_instructions = .; ++ .smp_altinstructions : AT(ADDR(.smp_altinstructions) - LOAD_OFFSET) { ++ *(.smp_altinstructions) ++ } ++ __smp_alt_instructions_end = .; ++ . = ALIGN(8); ++ __smp_locks = .; ++ .smp_locks : AT(ADDR(.smp_locks) - LOAD_OFFSET) { ++ *(.smp_locks) ++ } ++ __smp_locks_end = .; ++ .smp_altinstr_replacement : AT(ADDR(.smp_altinstr_replacement) - LOAD_OFFSET) { ++ *(.smp_altinstr_replacement) ++ } ++ . = ALIGN(4096); ++ __smp_alt_end = .; ++ + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .init.text : AT(ADDR(.init.text) - LOAD_OFFSET) { +diff -pruN linux-2.6.17-a/arch/x86_64/mm/init.c linux-2.6.17-b/arch/x86_64/mm/init.c +--- linux-2.6.17-a/arch/x86_64/mm/init.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/arch/x86_64/mm/init.c 2006-07-22 01:11:40.000000000 +0200 +@@ -644,20 +644,29 @@ void __init mem_init(void) + #endif + } + +-void free_initmem(void) ++void free_init_pages(char *what, unsigned long begin, unsigned long end) + { + unsigned long addr; + +- addr = (unsigned long)(&__init_begin); +- for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { ++ if (begin >= end) ++ return; ++ ++ printk(KERN_INFO "Freeing %s: %ldk freed\n", what, (end - begin) >> 10); ++ for (addr = begin; addr < end; addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + init_page_count(virt_to_page(addr)); + memset((void *)(addr & ~(PAGE_SIZE-1)), 0xcc, PAGE_SIZE); + free_page(addr); + totalram_pages++; + } ++} ++ ++void free_initmem(void) ++{ + memset(__initdata_begin, 0xba, __initdata_end - __initdata_begin); +- printk ("Freeing unused kernel memory: %luk freed\n", (__init_end - __init_begin) >> 10); ++ free_init_pages("unused kernel memory", ++ (unsigned long)(&__init_begin), ++ (unsigned long)(&__init_end)); + } + + #ifdef CONFIG_DEBUG_RODATA +@@ -686,15 +695,7 @@ void mark_rodata_ro(void) + #ifdef CONFIG_BLK_DEV_INITRD + void free_initrd_mem(unsigned long start, unsigned long end) + { +- if (start >= end) +- return; +- printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); +- for (; start < end; start += PAGE_SIZE) { +- ClearPageReserved(virt_to_page(start)); +- init_page_count(virt_to_page(start)); +- free_page(start); +- totalram_pages++; +- } ++ free_init_pages("initrd memory", start, end); + } + #endif + +diff -pruN linux-2.6.17-a/include/asm-i386/alternative.h linux-2.6.17-b/include/asm-i386/alternative.h +--- linux-2.6.17-a/include/asm-i386/alternative.h 2006-07-22 01:25:12.000000000 +0200 ++++ linux-2.6.17-b/include/asm-i386/alternative.h 2006-07-22 01:25:19.000000000 +0200 +@@ -3,6 +3,8 @@ + + #ifdef __KERNEL__ + ++#include ++ + #include + + struct alt_instr { +diff -pruN linux-2.6.17-a/include/asm-x86_64/alternative.h linux-2.6.17-b/include/asm-x86_64/alternative.h +--- linux-2.6.17-a/include/asm-x86_64/alternative.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.17-b/include/asm-x86_64/alternative.h 2006-07-22 01:11:40.000000000 +0200 +@@ -0,0 +1,146 @@ ++#ifndef _X86_64_ALTERNATIVE_H ++#define _X86_64_ALTERNATIVE_H ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++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 pad[5]; ++}; ++ ++extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end); ++ ++struct module; ++extern void alternatives_smp_module_add(struct module *mod, char *name, ++ void *locks, void *locks_end, ++ void *text, void *text_end); ++extern void alternatives_smp_module_del(struct module *mod); ++extern void alternatives_smp_switch(int smp); ++ ++#endif ++ ++/* ++ * Alternative instructions for different CPU types or capabilities. ++ * ++ * This allows to use optimized instructions even on generic binary ++ * kernels. ++ * ++ * length of oldinstr must be longer or equal the length of newinstr ++ * It can be padded with nops as needed. ++ * ++ * For non barrier like inlines please define new variants ++ * without volatile and memory clobber. ++ */ ++#define alternative(oldinstr, newinstr, feature) \ ++ asm volatile ("661:\n\t" oldinstr "\n662:\n" \ ++ ".section .altinstructions,\"a\"\n" \ ++ " .align 8\n" \ ++ " .quad 661b\n" /* label */ \ ++ " .quad 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 make 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 8\n" \ ++ " .quad 661b\n" /* label */ \ ++ " .quad 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) ++ ++/* Like alternative_input, but with a single output argument */ ++#define alternative_io(oldinstr, newinstr, feature, output, input...) \ ++ asm volatile ("661:\n\t" oldinstr "\n662:\n" \ ++ ".section .altinstructions,\"a\"\n" \ ++ " .align 8\n" \ ++ " .quad 661b\n" /* label */ \ ++ " .quad 663f\n" /* new instruction */ \ ++ " .byte %c[feat]\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" : output : [feat] "i" (feature), ##input) ++ ++/* ++ * Alternative inline assembly for SMP. ++ * ++ * alternative_smp() takes two versions (SMP first, UP second) and is ++ * for more complex stuff such as spinlocks. ++ * ++ * The LOCK_PREFIX macro defined here replaces the LOCK and ++ * LOCK_PREFIX macros used everywhere in the source tree. ++ * ++ * SMP alternatives use the same data structures as the other ++ * alternatives and the X86_FEATURE_UP flag to indicate the case of a ++ * UP system running a SMP kernel. The existing apply_alternatives() ++ * works fine for patching a SMP kernel for UP. ++ * ++ * The SMP alternative tables can be kept after boot and contain both ++ * UP and SMP versions of the instructions to allow switching back to ++ * SMP at runtime, when hotplugging in a new CPU, which is especially ++ * useful in virtualized environments. ++ * ++ * The very common lock prefix is handled as special case in a ++ * separate table which is a pure address list without replacement ptr ++ * and size information. That keeps the table sizes small. ++ */ ++ ++#ifdef CONFIG_SMP ++#define alternative_smp(smpinstr, upinstr, args...) \ ++ asm volatile ("661:\n\t" smpinstr "\n662:\n" \ ++ ".section .smp_altinstructions,\"a\"\n" \ ++ " .align 8\n" \ ++ " .quad 661b\n" /* label */ \ ++ " .quad 663f\n" /* new instruction */ \ ++ " .byte 0x66\n" /* X86_FEATURE_UP */ \ ++ " .byte 662b-661b\n" /* sourcelen */ \ ++ " .byte 664f-663f\n" /* replacementlen */ \ ++ ".previous\n" \ ++ ".section .smp_altinstr_replacement,\"awx\"\n" \ ++ "663:\n\t" upinstr "\n" /* replacement */ \ ++ "664:\n\t.fill 662b-661b,1,0x42\n" /* space for original */ \ ++ ".previous" : args) ++ ++#define LOCK_PREFIX \ ++ ".section .smp_locks,\"a\"\n" \ ++ " .align 8\n" \ ++ " .quad 661f\n" /* address */ \ ++ ".previous\n" \ ++ "661:\n\tlock; " ++ ++#else /* ! CONFIG_SMP */ ++#define alternative_smp(smpinstr, upinstr, args...) \ ++ asm volatile (upinstr : args) ++#define LOCK_PREFIX "" ++#endif ++ ++#endif /* _X86_64_ALTERNATIVE_H */ +diff -pruN linux-2.6.17-a/include/asm-x86_64/atomic.h linux-2.6.17-b/include/asm-x86_64/atomic.h +--- linux-2.6.17-a/include/asm-x86_64/atomic.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/include/asm-x86_64/atomic.h 2006-07-22 01:12:39.000000000 +0200 +@@ -2,7 +2,7 @@ + #define __ARCH_X86_64_ATOMIC__ + + #include +-#include ++#include + + /* atomic_t should be 32 bit signed type */ + +@@ -53,7 +53,7 @@ typedef struct { volatile int counter; } + static __inline__ void atomic_add(int i, atomic_t *v) + { + __asm__ __volatile__( +- LOCK "addl %1,%0" ++ LOCK_PREFIX "addl %1,%0" + :"=m" (v->counter) + :"ir" (i), "m" (v->counter)); + } +@@ -68,7 +68,7 @@ static __inline__ void atomic_add(int i, + static __inline__ void atomic_sub(int i, atomic_t *v) + { + __asm__ __volatile__( +- LOCK "subl %1,%0" ++ LOCK_PREFIX "subl %1,%0" + :"=m" (v->counter) + :"ir" (i), "m" (v->counter)); + } +@@ -87,7 +87,7 @@ static __inline__ int atomic_sub_and_tes + unsigned char c; + + __asm__ __volatile__( +- LOCK "subl %2,%0; sete %1" ++ LOCK_PREFIX "subl %2,%0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"ir" (i), "m" (v->counter) : "memory"); + return c; +@@ -102,7 +102,7 @@ static __inline__ int atomic_sub_and_tes + static __inline__ void atomic_inc(atomic_t *v) + { + __asm__ __volatile__( +- LOCK "incl %0" ++ LOCK_PREFIX "incl %0" + :"=m" (v->counter) + :"m" (v->counter)); + } +@@ -116,7 +116,7 @@ static __inline__ void atomic_inc(atomic + static __inline__ void atomic_dec(atomic_t *v) + { + __asm__ __volatile__( +- LOCK "decl %0" ++ LOCK_PREFIX "decl %0" + :"=m" (v->counter) + :"m" (v->counter)); + } +@@ -134,7 +134,7 @@ static __inline__ int atomic_dec_and_tes + unsigned char c; + + __asm__ __volatile__( +- LOCK "decl %0; sete %1" ++ LOCK_PREFIX "decl %0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"m" (v->counter) : "memory"); + return c != 0; +@@ -153,7 +153,7 @@ static __inline__ int atomic_inc_and_tes + unsigned char c; + + __asm__ __volatile__( +- LOCK "incl %0; sete %1" ++ LOCK_PREFIX "incl %0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"m" (v->counter) : "memory"); + return c != 0; +@@ -173,7 +173,7 @@ static __inline__ int atomic_add_negativ + unsigned char c; + + __asm__ __volatile__( +- LOCK "addl %2,%0; sets %1" ++ LOCK_PREFIX "addl %2,%0; sets %1" + :"=m" (v->counter), "=qm" (c) + :"ir" (i), "m" (v->counter) : "memory"); + return c; +@@ -190,7 +190,7 @@ static __inline__ int atomic_add_return( + { + int __i = i; + __asm__ __volatile__( +- LOCK "xaddl %0, %1;" ++ LOCK_PREFIX "xaddl %0, %1;" + :"=r"(i) + :"m"(v->counter), "0"(i)); + return i + __i; +@@ -238,7 +238,7 @@ typedef struct { volatile long counter; + static __inline__ void atomic64_add(long i, atomic64_t *v) + { + __asm__ __volatile__( +- LOCK "addq %1,%0" ++ LOCK_PREFIX "addq %1,%0" + :"=m" (v->counter) + :"ir" (i), "m" (v->counter)); + } +@@ -253,7 +253,7 @@ static __inline__ void atomic64_add(long + static __inline__ void atomic64_sub(long i, atomic64_t *v) + { + __asm__ __volatile__( +- LOCK "subq %1,%0" ++ LOCK_PREFIX "subq %1,%0" + :"=m" (v->counter) + :"ir" (i), "m" (v->counter)); + } +@@ -272,7 +272,7 @@ static __inline__ int atomic64_sub_and_t + unsigned char c; + + __asm__ __volatile__( +- LOCK "subq %2,%0; sete %1" ++ LOCK_PREFIX "subq %2,%0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"ir" (i), "m" (v->counter) : "memory"); + return c; +@@ -287,7 +287,7 @@ static __inline__ int atomic64_sub_and_t + static __inline__ void atomic64_inc(atomic64_t *v) + { + __asm__ __volatile__( +- LOCK "incq %0" ++ LOCK_PREFIX "incq %0" + :"=m" (v->counter) + :"m" (v->counter)); + } +@@ -301,7 +301,7 @@ static __inline__ void atomic64_inc(atom + static __inline__ void atomic64_dec(atomic64_t *v) + { + __asm__ __volatile__( +- LOCK "decq %0" ++ LOCK_PREFIX "decq %0" + :"=m" (v->counter) + :"m" (v->counter)); + } +@@ -319,7 +319,7 @@ static __inline__ int atomic64_dec_and_t + unsigned char c; + + __asm__ __volatile__( +- LOCK "decq %0; sete %1" ++ LOCK_PREFIX "decq %0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"m" (v->counter) : "memory"); + return c != 0; +@@ -338,7 +338,7 @@ static __inline__ int atomic64_inc_and_t + unsigned char c; + + __asm__ __volatile__( +- LOCK "incq %0; sete %1" ++ LOCK_PREFIX "incq %0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"m" (v->counter) : "memory"); + return c != 0; +@@ -358,7 +358,7 @@ static __inline__ int atomic64_add_negat + unsigned char c; + + __asm__ __volatile__( +- LOCK "addq %2,%0; sets %1" ++ LOCK_PREFIX "addq %2,%0; sets %1" + :"=m" (v->counter), "=qm" (c) + :"ir" (i), "m" (v->counter) : "memory"); + return c; +@@ -375,7 +375,7 @@ static __inline__ long atomic64_add_retu + { + long __i = i; + __asm__ __volatile__( +- LOCK "xaddq %0, %1;" ++ LOCK_PREFIX "xaddq %0, %1;" + :"=r"(i) + :"m"(v->counter), "0"(i)); + return i + __i; +@@ -419,11 +419,11 @@ static __inline__ long atomic64_sub_retu + + /* These are x86-specific, used by some header files */ + #define atomic_clear_mask(mask, addr) \ +-__asm__ __volatile__(LOCK "andl %0,%1" \ ++__asm__ __volatile__(LOCK_PREFIX "andl %0,%1" \ + : : "r" (~(mask)),"m" (*addr) : "memory") + + #define atomic_set_mask(mask, addr) \ +-__asm__ __volatile__(LOCK "orl %0,%1" \ ++__asm__ __volatile__(LOCK_PREFIX "orl %0,%1" \ + : : "r" ((unsigned)mask),"m" (*(addr)) : "memory") + + /* Atomic operations are already serializing on x86 */ +diff -pruN linux-2.6.17-a/include/asm-x86_64/bitops.h linux-2.6.17-b/include/asm-x86_64/bitops.h +--- linux-2.6.17-a/include/asm-x86_64/bitops.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/include/asm-x86_64/bitops.h 2006-07-22 01:13:50.000000000 +0200 +@@ -6,12 +6,7 @@ + */ + + #include +- +-#ifdef CONFIG_SMP +-#define LOCK_PREFIX "lock ; " +-#else +-#define LOCK_PREFIX "" +-#endif ++#include + + #define ADDR (*(volatile long *) addr) + +diff -pruN linux-2.6.17-a/include/asm-x86_64/cpufeature.h linux-2.6.17-b/include/asm-x86_64/cpufeature.h +--- linux-2.6.17-a/include/asm-x86_64/cpufeature.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/include/asm-x86_64/cpufeature.h 2006-07-22 01:11:40.000000000 +0200 +@@ -65,6 +65,8 @@ + #define X86_FEATURE_CONSTANT_TSC (3*32+5) /* TSC runs at constant rate */ + #define X86_FEATURE_SYNC_RDTSC (3*32+6) /* RDTSC syncs CPU core */ + #define X86_FEATURE_FXSAVE_LEAK (3*32+7) /* FIP/FOP/FDP leaks through FXSAVE */ ++#define X86_FEATURE_UP (3*32+8) /* SMP kernel running on UP */ ++ + + /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ + #define X86_FEATURE_XMM3 (4*32+ 0) /* Streaming SIMD Extensions-3 */ +diff -pruN linux-2.6.17-a/include/asm-x86_64/mutex.h linux-2.6.17-b/include/asm-x86_64/mutex.h +--- linux-2.6.17-a/include/asm-x86_64/mutex.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/include/asm-x86_64/mutex.h 2006-07-22 01:11:40.000000000 +0200 +@@ -24,7 +24,7 @@ do { \ + typecheck_fn(fastcall void (*)(atomic_t *), fail_fn); \ + \ + __asm__ __volatile__( \ +- LOCK " decl (%%rdi) \n" \ ++ LOCK_PREFIX " decl (%%rdi) \n" \ + " js 2f \n" \ + "1: \n" \ + \ +@@ -74,7 +74,7 @@ do { \ + typecheck_fn(fastcall void (*)(atomic_t *), fail_fn); \ + \ + __asm__ __volatile__( \ +- LOCK " incl (%%rdi) \n" \ ++ LOCK_PREFIX " incl (%%rdi) \n" \ + " jle 2f \n" \ + "1: \n" \ + \ +diff -pruN linux-2.6.17-a/include/asm-x86_64/rwlock.h linux-2.6.17-b/include/asm-x86_64/rwlock.h +--- linux-2.6.17-a/include/asm-x86_64/rwlock.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/include/asm-x86_64/rwlock.h 2006-07-22 01:11:40.000000000 +0200 +@@ -24,7 +24,7 @@ + #define RW_LOCK_BIAS_STR "0x01000000" + + #define __build_read_lock_ptr(rw, helper) \ +- asm volatile(LOCK "subl $1,(%0)\n\t" \ ++ asm volatile(LOCK_PREFIX "subl $1,(%0)\n\t" \ + "js 2f\n" \ + "1:\n" \ + LOCK_SECTION_START("") \ +@@ -34,7 +34,7 @@ + ::"a" (rw) : "memory") + + #define __build_read_lock_const(rw, helper) \ +- asm volatile(LOCK "subl $1,%0\n\t" \ ++ asm volatile(LOCK_PREFIX "subl $1,%0\n\t" \ + "js 2f\n" \ + "1:\n" \ + LOCK_SECTION_START("") \ +@@ -54,7 +54,7 @@ + } while (0) + + #define __build_write_lock_ptr(rw, helper) \ +- asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \ ++ asm volatile(LOCK_PREFIX "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \ + "jnz 2f\n" \ + "1:\n" \ + LOCK_SECTION_START("") \ +@@ -64,7 +64,7 @@ + ::"a" (rw) : "memory") + + #define __build_write_lock_const(rw, helper) \ +- asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",%0\n\t" \ ++ asm volatile(LOCK_PREFIX "subl $" RW_LOCK_BIAS_STR ",%0\n\t" \ + "jnz 2f\n" \ + "1:\n" \ + LOCK_SECTION_START("") \ +diff -pruN linux-2.6.17-a/include/asm-x86_64/semaphore.h linux-2.6.17-b/include/asm-x86_64/semaphore.h +--- linux-2.6.17-a/include/asm-x86_64/semaphore.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/include/asm-x86_64/semaphore.h 2006-07-22 01:11:40.000000000 +0200 +@@ -106,7 +106,7 @@ static inline void down(struct semaphore + + __asm__ __volatile__( + "# atomic down operation\n\t" +- LOCK "decl %0\n\t" /* --sem->count */ ++ LOCK_PREFIX "decl %0\n\t" /* --sem->count */ + "js 2f\n" + "1:\n" + LOCK_SECTION_START("") +@@ -130,7 +130,7 @@ static inline int down_interruptible(str + + __asm__ __volatile__( + "# atomic interruptible down operation\n\t" +- LOCK "decl %1\n\t" /* --sem->count */ ++ LOCK_PREFIX "decl %1\n\t" /* --sem->count */ + "js 2f\n\t" + "xorl %0,%0\n" + "1:\n" +@@ -154,7 +154,7 @@ static inline int down_trylock(struct se + + __asm__ __volatile__( + "# atomic interruptible down operation\n\t" +- LOCK "decl %1\n\t" /* --sem->count */ ++ LOCK_PREFIX "decl %1\n\t" /* --sem->count */ + "js 2f\n\t" + "xorl %0,%0\n" + "1:\n" +@@ -178,7 +178,7 @@ static inline void up(struct semaphore * + { + __asm__ __volatile__( + "# atomic up operation\n\t" +- LOCK "incl %0\n\t" /* ++sem->count */ ++ LOCK_PREFIX "incl %0\n\t" /* ++sem->count */ + "jle 2f\n" + "1:\n" + LOCK_SECTION_START("") +diff -pruN linux-2.6.17-a/include/asm-x86_64/spinlock.h linux-2.6.17-b/include/asm-x86_64/spinlock.h +--- linux-2.6.17-a/include/asm-x86_64/spinlock.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/include/asm-x86_64/spinlock.h 2006-07-22 01:11:40.000000000 +0200 +@@ -32,15 +32,19 @@ + "jmp 1b\n" \ + LOCK_SECTION_END + ++#define __raw_spin_lock_string_up \ ++ "\n\tdecl %0" ++ + #define __raw_spin_unlock_string \ + "movl $1,%0" \ + :"=m" (lock->slock) : : "memory" + + static inline void __raw_spin_lock(raw_spinlock_t *lock) + { +- __asm__ __volatile__( +- __raw_spin_lock_string +- :"=m" (lock->slock) : : "memory"); ++ alternative_smp( ++ __raw_spin_lock_string, ++ __raw_spin_lock_string_up, ++ "=m" (lock->slock) : : "memory"); + } + + #define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock) +diff -pruN linux-2.6.17-a/include/asm-x86_64/system.h linux-2.6.17-b/include/asm-x86_64/system.h +--- linux-2.6.17-a/include/asm-x86_64/system.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-b/include/asm-x86_64/system.h 2006-07-22 01:11:40.000000000 +0200 +@@ -4,15 +4,10 @@ + #include + #include + #include ++#include + + #ifdef __KERNEL__ + +-#ifdef CONFIG_SMP +-#define LOCK_PREFIX "lock ; " +-#else +-#define LOCK_PREFIX "" +-#endif +- + #define __STR(x) #x + #define STR(x) __STR(x) + +@@ -35,7 +30,7 @@ + "thread_return:\n\t" \ + "movq %%gs:%P[pda_pcurrent],%%rsi\n\t" \ + "movq %P[thread_info](%%rsi),%%r8\n\t" \ +- LOCK "btr %[tif_fork],%P[ti_flags](%%r8)\n\t" \ ++ LOCK_PREFIX "btr %[tif_fork],%P[ti_flags](%%r8)\n\t" \ + "movq %%rax,%%rdi\n\t" \ + "jc ret_from_fork\n\t" \ + RESTORE_CONTEXT \ +@@ -70,82 +65,6 @@ extern void load_gs_index(unsigned); + ".previous" \ + : :"r" (value), "r" (0)) + +-#ifdef __KERNEL__ +-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 pad[5]; +-}; +-#endif +- +-/* +- * Alternative instructions for different CPU types or capabilities. +- * +- * This allows to use optimized instructions even on generic binary +- * kernels. +- * +- * length of oldinstr must be longer or equal the length of newinstr +- * It can be padded with nops as needed. +- * +- * For non barrier like inlines please define new variants +- * without volatile and memory clobber. +- */ +-#define alternative(oldinstr, newinstr, feature) \ +- asm volatile ("661:\n\t" oldinstr "\n662:\n" \ +- ".section .altinstructions,\"a\"\n" \ +- " .align 8\n" \ +- " .quad 661b\n" /* label */ \ +- " .quad 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. +- * +- * Peculiarities: +- * 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 make 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 8\n" \ +- " .quad 661b\n" /* label */ \ +- " .quad 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) +- +-/* Like alternative_input, but with a single output argument */ +-#define alternative_io(oldinstr, newinstr, feature, output, input...) \ +- asm volatile ("661:\n\t" oldinstr "\n662:\n" \ +- ".section .altinstructions,\"a\"\n" \ +- " .align 8\n" \ +- " .quad 661b\n" /* label */ \ +- " .quad 663f\n" /* new instruction */ \ +- " .byte %c[feat]\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" : output : [feat] "i" (feature), ##input) +- + /* + * Clear and set 'TS' bit respectively + */ +@@ -367,5 +286,6 @@ static inline unsigned long __cmpxchg(vo + void cpu_idle_wait(void); + + extern unsigned long arch_align_stack(unsigned long sp); ++extern void free_init_pages(char *what, unsigned long begin, unsigned long end); + + #endif