diff options
author | Tejun Heo <tj@kernel.org> | 2009-01-13 06:41:35 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-01-16 08:19:46 -0500 |
commit | 1a51e3a0aed18767cf2762e95456ecfeb0bca5e6 (patch) | |
tree | 2d930218ef1072a59f7dac0f97bb03aa02796c8c /arch/x86/kernel/setup_percpu.c | |
parent | c8f3329a0ddd751241e96b4100df7eda14b2cbc6 (diff) |
x86: fold pda into percpu area on SMP
[ Based on original patch from Christoph Lameter and Mike Travis. ]
Currently pdas and percpu areas are allocated separately. %gs points
to local pda and percpu area can be reached using pda->data_offset.
This patch folds pda into percpu area.
Due to strange gcc requirement, pda needs to be at the beginning of
the percpu area so that pda->stack_canary is at %gs:40. To achieve
this, a new percpu output section macro - PERCPU_VADDR_PREALLOC() - is
added and used to reserve pda sized chunk at the start of the percpu
area.
After this change, for boot cpu, %gs first points to pda in the
data.init area and later during setup_per_cpu_areas() gets updated to
point to the actual pda. This means that setup_per_cpu_areas() need
to reload %gs for CPU0 while clearing pda area for other cpus as cpu0
already has modified it when control reaches setup_per_cpu_areas().
This patch also removes now unnecessary get_local_pda() and its call
sites.
A lot of this patch is taken from Mike Travis' "x86_64: Fold pda into
per cpu area" patch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/setup_percpu.c')
-rw-r--r-- | arch/x86/kernel/setup_percpu.c | 107 |
1 files changed, 46 insertions, 61 deletions
diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c index 73ab01b297c5..63d462802272 100644 --- a/arch/x86/kernel/setup_percpu.c +++ b/arch/x86/kernel/setup_percpu.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <asm/mpspec.h> | 13 | #include <asm/mpspec.h> |
14 | #include <asm/apicdef.h> | 14 | #include <asm/apicdef.h> |
15 | #include <asm/highmem.h> | 15 | #include <asm/highmem.h> |
16 | #include <asm/proto.h> | ||
16 | #include <asm/cpumask.h> | 17 | #include <asm/cpumask.h> |
17 | 18 | ||
18 | #ifdef CONFIG_DEBUG_PER_CPU_MAPS | 19 | #ifdef CONFIG_DEBUG_PER_CPU_MAPS |
@@ -65,6 +66,36 @@ static void __init setup_node_to_cpumask_map(void); | |||
65 | static inline void setup_node_to_cpumask_map(void) { } | 66 | static inline void setup_node_to_cpumask_map(void) { } |
66 | #endif | 67 | #endif |
67 | 68 | ||
69 | #ifdef CONFIG_X86_64 | ||
70 | void __cpuinit load_pda_offset(int cpu) | ||
71 | { | ||
72 | /* Memory clobbers used to order pda/percpu accesses */ | ||
73 | mb(); | ||
74 | wrmsrl(MSR_GS_BASE, cpu_pda(cpu)); | ||
75 | mb(); | ||
76 | } | ||
77 | |||
78 | #endif /* CONFIG_SMP && CONFIG_X86_64 */ | ||
79 | |||
80 | #ifdef CONFIG_X86_64 | ||
81 | |||
82 | /* correctly size the local cpu masks */ | ||
83 | static void setup_cpu_local_masks(void) | ||
84 | { | ||
85 | alloc_bootmem_cpumask_var(&cpu_initialized_mask); | ||
86 | alloc_bootmem_cpumask_var(&cpu_callin_mask); | ||
87 | alloc_bootmem_cpumask_var(&cpu_callout_mask); | ||
88 | alloc_bootmem_cpumask_var(&cpu_sibling_setup_mask); | ||
89 | } | ||
90 | |||
91 | #else /* CONFIG_X86_32 */ | ||
92 | |||
93 | static inline void setup_cpu_local_masks(void) | ||
94 | { | ||
95 | } | ||
96 | |||
97 | #endif /* CONFIG_X86_32 */ | ||
98 | |||
68 | #ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA | 99 | #ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA |
69 | /* | 100 | /* |
70 | * Copy data used in early init routines from the initial arrays to the | 101 | * Copy data used in early init routines from the initial arrays to the |
@@ -101,63 +132,7 @@ static void __init setup_per_cpu_maps(void) | |||
101 | */ | 132 | */ |
102 | unsigned long __per_cpu_offset[NR_CPUS] __read_mostly; | 133 | unsigned long __per_cpu_offset[NR_CPUS] __read_mostly; |
103 | EXPORT_SYMBOL(__per_cpu_offset); | 134 | EXPORT_SYMBOL(__per_cpu_offset); |
104 | static inline void setup_cpu_pda_map(void) { } | 135 | #endif |
105 | |||
106 | #elif !defined(CONFIG_SMP) | ||
107 | static inline void setup_cpu_pda_map(void) { } | ||
108 | |||
109 | #else /* CONFIG_SMP && CONFIG_X86_64 */ | ||
110 | |||
111 | /* | ||
112 | * Allocate cpu_pda pointer table and array via alloc_bootmem. | ||
113 | */ | ||
114 | static void __init setup_cpu_pda_map(void) | ||
115 | { | ||
116 | char *pda; | ||
117 | unsigned long size; | ||
118 | int cpu; | ||
119 | |||
120 | size = roundup(sizeof(struct x8664_pda), cache_line_size()); | ||
121 | |||
122 | /* allocate cpu_pda array and pointer table */ | ||
123 | { | ||
124 | unsigned long asize = size * (nr_cpu_ids - 1); | ||
125 | |||
126 | pda = alloc_bootmem(asize); | ||
127 | } | ||
128 | |||
129 | /* initialize pointer table to static pda's */ | ||
130 | for_each_possible_cpu(cpu) { | ||
131 | if (cpu == 0) { | ||
132 | /* leave boot cpu pda in place */ | ||
133 | continue; | ||
134 | } | ||
135 | cpu_pda(cpu) = (struct x8664_pda *)pda; | ||
136 | cpu_pda(cpu)->in_bootmem = 1; | ||
137 | pda += size; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | #endif /* CONFIG_SMP && CONFIG_X86_64 */ | ||
142 | |||
143 | #ifdef CONFIG_X86_64 | ||
144 | |||
145 | /* correctly size the local cpu masks */ | ||
146 | static void setup_cpu_local_masks(void) | ||
147 | { | ||
148 | alloc_bootmem_cpumask_var(&cpu_initialized_mask); | ||
149 | alloc_bootmem_cpumask_var(&cpu_callin_mask); | ||
150 | alloc_bootmem_cpumask_var(&cpu_callout_mask); | ||
151 | alloc_bootmem_cpumask_var(&cpu_sibling_setup_mask); | ||
152 | } | ||
153 | |||
154 | #else /* CONFIG_X86_32 */ | ||
155 | |||
156 | static inline void setup_cpu_local_masks(void) | ||
157 | { | ||
158 | } | ||
159 | |||
160 | #endif /* CONFIG_X86_32 */ | ||
161 | 136 | ||
162 | /* | 137 | /* |
163 | * Great future plan: | 138 | * Great future plan: |
@@ -171,9 +146,6 @@ void __init setup_per_cpu_areas(void) | |||
171 | int cpu; | 146 | int cpu; |
172 | unsigned long align = 1; | 147 | unsigned long align = 1; |
173 | 148 | ||
174 | /* Setup cpu_pda map */ | ||
175 | setup_cpu_pda_map(); | ||
176 | |||
177 | /* Copy section for each CPU (we discard the original) */ | 149 | /* Copy section for each CPU (we discard the original) */ |
178 | old_size = PERCPU_ENOUGH_ROOM; | 150 | old_size = PERCPU_ENOUGH_ROOM; |
179 | align = max_t(unsigned long, PAGE_SIZE, align); | 151 | align = max_t(unsigned long, PAGE_SIZE, align); |
@@ -204,8 +176,21 @@ void __init setup_per_cpu_areas(void) | |||
204 | cpu, node, __pa(ptr)); | 176 | cpu, node, __pa(ptr)); |
205 | } | 177 | } |
206 | #endif | 178 | #endif |
207 | per_cpu_offset(cpu) = ptr - __per_cpu_start; | 179 | |
208 | memcpy(ptr, __per_cpu_load, __per_cpu_end - __per_cpu_start); | 180 | memcpy(ptr, __per_cpu_load, __per_cpu_end - __per_cpu_start); |
181 | #ifdef CONFIG_X86_64 | ||
182 | cpu_pda(cpu) = (void *)ptr; | ||
183 | |||
184 | /* | ||
185 | * CPU0 modified pda in the init data area, reload pda | ||
186 | * offset for CPU0 and clear the area for others. | ||
187 | */ | ||
188 | if (cpu == 0) | ||
189 | load_pda_offset(0); | ||
190 | else | ||
191 | memset(cpu_pda(cpu), 0, sizeof(*cpu_pda(cpu))); | ||
192 | #endif | ||
193 | per_cpu_offset(cpu) = ptr - __per_cpu_start; | ||
209 | 194 | ||
210 | DBG("PERCPU: cpu %4d %p\n", cpu, ptr); | 195 | DBG("PERCPU: cpu %4d %p\n", cpu, ptr); |
211 | } | 196 | } |