SMP alternatives, take #2
PATCH 1/1 SMP alternatives, take #2
Hi,
Here next version of the SMP alternatives patch.
Had a more detailed look at the bits generated, the vast majority in the smp alternatives table where simply lock prefixes. So I decided to specical-case these to keep the table sizes small. There are three elf sections now:
.smp_altinstructions
.smp_altinstr_replacement
These are pretty much the same like the existing non-smp alternative bits, with the exception that the replacement table also has some space to keep the original instruction, to allow patching both SMP->UP and back UP->SMP. It's used to patch the code for spinlocks with simpler UP versions.
.smp_locks
List of pointers to the lock prefixes to be NOP'ed for UP.
For the core kernel these three sections are grouped together and are page-aligned, so they can be freed. Freeing happens automatically in some cases where they not needed any more after booting up (UP system found, kernel without CONFIG_HOTPLUG_CPU). There is also a kernel cmd line option to force the tables being released.
For modules only the .smp_locks section is handled, the other ones simply don't appear in modules ;)
cheers,
Gerd
arch/i386/kernel/Makefile +1 -1
@@ -7,7 +7,7 @@ obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o time.o ioport.o ldt.o setup.o i8259.o sys_i386.o \ pci-dma.o i386_ksyms.o i387.o dmi_scan.o bootflag.o \ - doublefault.o quirks.o i8237.o + doublefault.o quirks.o i8237.o alternative.o obj-y += cpu/ obj-y += timers/
arch/i386/kernel/alternative.c +320 -0
320 lines not on this page · raw message
arch/i386/kernel/module.c +22 -10
@@ -104,26 +104,38 @@return -ENOEXEC ; } -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 *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); - } +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); }
arch/i386/kernel/semaphore.c +4 -4
@@ -110,11 +110,11 @@".align 4\n" ".globl __write_lock_failed\n" "__write_lock_failed:\n\t" -LOCK "addl $" RW_LOCK_BIAS_STR ",(%eax)\n" +LOCK_PREFIX "addl $" RW_LOCK_BIAS_STR ",(%eax)\n" "1: rep; nop\n\t" "cmpl $" RW_LOCK_BIAS_STR ",(%eax)\n\t" "jne 1b\n\t" -LOCK "subl $" RW_LOCK_BIAS_STR ",(%eax)\n\t" +LOCK_PREFIX "subl $" RW_LOCK_BIAS_STR ",(%eax)\n\t" "jnz __write_lock_failed\n\t" "ret" );
@@ -124,11 +124,11 @@".align 4\n" ".globl __read_lock_failed\n" "__read_lock_failed:\n\t" -LOCK "incl (%eax)\n" +LOCK_PREFIX "incl (%eax)\n" "1: rep; nop\n\t" "cmpl $1,(%eax)\n\t" "js 1b\n\t" -LOCK "decl (%eax)\n\t" +LOCK_PREFIX "decl (%eax)\n\t" "js __read_lock_failed\n\t" "ret" );
arch/i386/kernel/setup.c +0 -95
101 lines not on this page · raw message
arch/i386/kernel/smpboot.c +4 -0
@@ -874,6 +874,7 @@unsigned short nmi_high =0 , nmi_low =0 ; ++cpucount; +alternatives_smp_switch (1 );/* * We can't use kernel_thread since we must avoid to
@@ -1315,6 +1316,9 @@fixup_irqs (map);/* It's now safe to remove this processor from the online map */ cpu_clear (cpu, cpu_online_map); + +if (1 ==num_online_cpus ()) +alternatives_smp_switch (0 );return 0 ; }
arch/i386/kernel/vmlinux.lds.S +20 -0
@@ -68,6 +68,26 @@ *(.data.init_task) } + /* 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(4); + __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 = .; + /* will be freed after init */ . = ALIGN(4096); /* Init code and data */ __init_begin = .;
arch/i386/mm/init.c +11 -12
@@ -667,31 +667,30 @@return flag; } -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 ) { +for (addr = begin; addr < end; addr +=PAGE_SIZE ) {ClearPageReserved (virt_to_page (addr));set_page_count (virt_to_page (addr),1 );memset ((void *)addr,0xcc ,PAGE_SIZE );free_page (addr); totalram_pages++; } -printk (KERN_INFO "Freeing unused kernel memory: %dk freed\n" , (__init_end - __init_begin) >>10 ); +printk (KERN_INFO "Freeing %s: %ldk freed\n" , what, (end - begin) >>10 ); +} + +void free_initmem (void ) +{ +free_init_pages ("unused kernel memory" , + (unsigned long )(&__init_begin), + (unsigned long )(&__init_end)); }#ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem (unsigned long start,unsigned long end) { -if (start < end) -printk (KERN_INFO "Freeing initrd memory: %ldk freed\n" , (end - start) >>10 ); -for (; start < end; start +=PAGE_SIZE ) { -ClearPageReserved (virt_to_page (start)); -set_page_count (virt_to_page (start),1 ); -free_page (start); - totalram_pages++; - } +free_init_pages ("initrd memory" , start, end); }#endif
include/asm-i386/alternative.h +129 -0
129 lines not on this page · raw message
include/asm-i386/atomic.h +11 -17
@@ -10,12 +10,6 @@* resource counting etc.. */ -#ifdef CONFIG_SMP -#define LOCK "lock ; " -#else -#define LOCK "" -#endif -/* * Make sure gcc doesn't try to be clever and move things around * on us. We need to use _exactly_ the address the user gave us,
@@ -52,7 +46,7 @@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)); }
@@ -67,7 +61,7 @@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)); }
@@ -86,7 +80,7 @@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;
@@ -101,7 +95,7 @@static __inline__void atomic_inc (atomic_t *v) { __asm__ __volatile__( -LOCK "incl %0" +LOCK_PREFIX "incl %0" :"=m" (v->counter) :"m" (v->counter)); }
@@ -115,7 +109,7 @@static __inline__void atomic_dec (atomic_t *v) { __asm__ __volatile__( -LOCK "decl %0" +LOCK_PREFIX "decl %0" :"=m" (v->counter) :"m" (v->counter)); }
@@ -133,7 +127,7 @@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 ;
@@ -152,7 +146,7 @@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 ;
@@ -172,7 +166,7 @@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;
@@ -195,7 +189,7 @@/* Modern 486+ processor */ __i = i; __asm__ __volatile__( -LOCK "xaddl %0, %1;" +LOCK_PREFIX "xaddl %0, %1;" :"=r" (i) :"m" (v->counter),"0" (i));return i + __i;
@@ -220,11 +214,11 @@/* 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" (mask),"m" (*(addr)) : "memory")/* Atomic operations are already serializing on x86 */
include/asm-i386/bitops.h +1 -6
@@ -7,6 +7,7 @@#include <linux/config.h> #include <linux/compiler.h> +#include <asm/alternative.h> /* * These have to be done with inline assembly: that way the bit-setting
@@ -16,12 +17,6 @@* bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). */ -#ifdef CONFIG_SMP -#define LOCK_PREFIX "lock ; " -#else -#define LOCK_PREFIX "" -#endif -#define ADDR (*(volatile long *) addr)/**
include/asm-i386/cpufeature.h +2 -0
@@ -70,6 +70,8 @@#define X86_FEATURE_P3 (3*32+ 6)/* P3 */ #define X86_FEATURE_P4 (3*32+ 7)/* P4 */ +#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 */ #define X86_FEATURE_MWAIT (4*32+ 3)/* Monitor/Mwait support */
include/asm-i386/rwlock.h +30 -26
72 lines not on this page · raw message
include/asm-i386/semaphore.h +4 -4
@@ -102,7 +102,7 @@might_sleep (); __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("")
@@ -126,7 +126,7 @@might_sleep (); __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"
@@ -151,7 +151,7 @@ __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"
@@ -176,7 +176,7 @@ { __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("")
include/asm-i386/spinlock.h +13 -8
@@ -48,18 +48,23 @@"jmp 1b\n" \ "4:\n\t" +#define __raw_spin_lock_string_up \ + "\n\tdecb %0" +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"); }static inline void __raw_spin_lock_flags (raw_spinlock_t *lock,unsigned long flags) { - __asm__ __volatile__( - __raw_spin_lock_string_flags - :"=m" (lock->slock) : "r" (flags) : "memory"); +alternative_smp ( + __raw_spin_lock_string_flags, + __raw_spin_lock_string_up, + "=m" (lock->slock) : "r" (flags) : "memory"); } static inline int __raw_spin_trylock(raw_spinlock_t *lock)
@@ -178,12 +183,12 @@static inline void __raw_read_unlock (raw_rwlock_t *rw) { - asmvolatile ("lock ; incl %0" :"=m" (rw->lock) : :"memory" ); + asmvolatile (LOCK_PREFIX "incl %0" :"=m" (rw->lock) : :"memory" ); }static inline void __raw_write_unlock (raw_rwlock_t *rw) { - asmvolatile ("lock ; addl $" RW_LOCK_BIAS_STR ", %0" + asmvolatile (LOCK_PREFIX "addl $" RW_LOCK_BIAS_STR ", %0" :"=m" (rw->lock) : :"memory" ); }
include/asm-i386/system.h +0 -61
67 lines not on this page · raw message