Name: per-cpu only for possible CPUs Author: Rusty Russell Status: Tested on 2.5.34 2-way i386 D: This allocates per-cpu areas only for those CPUs which may actually D: exist, before each one comes online. diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .3418-linux-2.5.38/init/main.c .3418-linux-2.5.38.updated/init/main.c --- .3418-linux-2.5.38/init/main.c 2002-09-21 13:55:19.000000000 +1000 +++ .3418-linux-2.5.38.updated/init/main.c 2002-09-23 10:53:02.000000000 +1000 @@ -306,32 +306,36 @@ static void __init smp_init(void) #define smp_init() do { } while (0) #endif -static inline void setup_per_cpu_areas(void) { } +static inline void setup_per_cpu_area(unsigned int cpu) { } static inline void smp_prepare_cpus(unsigned int maxcpus) { } #else #ifdef __GENERIC_PER_CPU +/* Created by linker magic */ +extern char __per_cpu_start[], __per_cpu_end[]; + unsigned long __per_cpu_offset[NR_CPUS]; -static void __init setup_per_cpu_areas(void) +/* Sets up per-cpu area for boot CPU. */ +static void __init setup_per_cpu_area(unsigned int cpu) { - unsigned long size, i; + unsigned long size; char *ptr; - /* Created by linker magic */ - extern char __per_cpu_start[], __per_cpu_end[]; /* Copy section for each CPU (we discard the original) */ size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES); if (!size) return; - ptr = alloc_bootmem(size * NR_CPUS); + /* First CPU happens really early... */ + if (cpu == smp_processor_id()) + ptr = alloc_bootmem(size); + else + ptr = kmalloc(size, GFP_ATOMIC); - for (i = 0; i < NR_CPUS; i++, ptr += size) { - __per_cpu_offset[i] = ptr - __per_cpu_start; - memcpy(ptr, __per_cpu_start, size); - } + __per_cpu_offset[cpu] = ptr - __per_cpu_start; + memcpy(ptr, __per_cpu_start, size); } #endif /* !__GENERIC_PER_CPU */ @@ -340,7 +344,16 @@ static void __init smp_init(void) { unsigned int i; - /* FIXME: This should be done in userspace --RR */ + for (i = 0; i < NR_CPUS; i++) { + if (cpu_possible(i)) { + if (i != smp_processor_id()) + setup_per_cpu_area(i); + } else { + /* Force a NULL deref on use */ + __per_cpu_offset[i] = (char *)0 - __per_cpu_start; + } + } + for (i = 0; i < NR_CPUS; i++) { if (num_online_cpus() >= max_cpus) break; @@ -392,7 +405,7 @@ asmlinkage void __init start_kernel(void lock_kernel(); printk(linux_banner); setup_arch(&command_line); - setup_per_cpu_areas(); + setup_per_cpu_area(smp_processor_id()); build_all_zonelists(); printk("Kernel command line: %s\n", saved_command_line); parse_options(command_line);