diff options
author | Dmitry Adamushko <dmitry.adamushko@gmail.com> | 2009-05-11 17:48:27 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-05-12 04:36:44 -0400 |
commit | 871b72dd1e12afc3f024479531d25a9339d2e3f9 (patch) | |
tree | 132f309ab587561f4da1cfe6592c570fbad67d53 /arch/x86/kernel/microcode_amd.c | |
parent | a4d7749be5de4a7261bcbe3c7d96c748792ec455 (diff) |
x86: microcode: use smp_call_function_single instead of set_cpus_allowed, cleanup of synchronization logic
* Solve issues described in 6f66cbc63081fd70e3191b4dbb796746780e5ae1
in a way that doesn't resort to set_cpus_allowed();
* in fact, only collect_cpu_info and apply_microcode callbacks
must run on a target cpu, others will do just fine on any other.
smp_call_function_single() (as suggested by Ingo) is used to run
these callbacks on a target cpu.
* cleanup of synchronization logic of the 'microcode_core' part
The generic 'microcode_core' part guarantees that only a single cpu
(be it a full-fledged cpu, one of the cores or HT)
is being updated at any particular moment of time.
In general, there is no need for any additional sync. mechanism in
arch-specific parts (the patch removes existing spinlocks).
See also the "Synchronization" section in microcode_core.c.
* return -EINVAL instead of -1 (which is translated into -EPERM) in
microcode_write(), reload_cpu() and mc_sysdev_add(). Other suggestions
for an error code?
* use 'enum ucode_state' as return value of request_microcode_{fw, user}
to gain more flexibility by distinguishing between real error cases
and situations when an appropriate ucode was not found (which is not an
error per-se).
* some minor cleanups
Thanks a lot to Hugh Dickins for review/suggestions/testing!
Reference: http://marc.info/?l=linux-kernel&m=124025889012541&w=2
[ Impact: refactor and clean up microcode driver locking code ]
Signed-off-by: Dmitry Adamushko <dmitry.adamushko@gmail.com>
Acked-by: Hugh Dickins <hugh@veritas.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Andreas Herrmann <andreas.herrmann3@amd.com>
Cc: Peter Oruba <peter.oruba@amd.com>
Cc: Arjan van de Ven <arjan@infradead.org>
LKML-Reference: <1242078507.5560.9.camel@earth>
[ did some more cleanups ]
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/include/asm/microcode.h | 25 ++
arch/x86/kernel/microcode_amd.c | 58 ++----
arch/x86/kernel/microcode_core.c | 326 +++++++++++++++++++++-----------------
arch/x86/kernel/microcode_intel.c | 92 +++-------
4 files changed, 261 insertions(+), 240 deletions(-)
(~20 new comment lines)
Diffstat (limited to 'arch/x86/kernel/microcode_amd.c')
-rw-r--r-- | arch/x86/kernel/microcode_amd.c | 58 |
1 files changed, 22 insertions, 36 deletions
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c index 453b5795a5c6..c8be20f16447 100644 --- a/arch/x86/kernel/microcode_amd.c +++ b/arch/x86/kernel/microcode_amd.c | |||
@@ -13,25 +13,13 @@ | |||
13 | * Licensed under the terms of the GNU General Public | 13 | * Licensed under the terms of the GNU General Public |
14 | * License version 2. See file COPYING for details. | 14 | * License version 2. See file COPYING for details. |
15 | */ | 15 | */ |
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/capability.h> | ||
18 | #include <linux/miscdevice.h> | ||
19 | #include <linux/firmware.h> | 16 | #include <linux/firmware.h> |
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/cpumask.h> | ||
22 | #include <linux/pci_ids.h> | 17 | #include <linux/pci_ids.h> |
23 | #include <linux/uaccess.h> | 18 | #include <linux/uaccess.h> |
24 | #include <linux/vmalloc.h> | 19 | #include <linux/vmalloc.h> |
25 | #include <linux/kernel.h> | 20 | #include <linux/kernel.h> |
26 | #include <linux/module.h> | 21 | #include <linux/module.h> |
27 | #include <linux/mutex.h> | ||
28 | #include <linux/sched.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <linux/cpu.h> | ||
32 | #include <linux/pci.h> | 22 | #include <linux/pci.h> |
33 | #include <linux/fs.h> | ||
34 | #include <linux/mm.h> | ||
35 | 23 | ||
36 | #include <asm/microcode.h> | 24 | #include <asm/microcode.h> |
37 | #include <asm/processor.h> | 25 | #include <asm/processor.h> |
@@ -79,9 +67,6 @@ struct microcode_amd { | |||
79 | #define UCODE_CONTAINER_SECTION_HDR 8 | 67 | #define UCODE_CONTAINER_SECTION_HDR 8 |
80 | #define UCODE_CONTAINER_HEADER_SIZE 12 | 68 | #define UCODE_CONTAINER_HEADER_SIZE 12 |
81 | 69 | ||
82 | /* serialize access to the physical write */ | ||
83 | static DEFINE_SPINLOCK(microcode_update_lock); | ||
84 | |||
85 | static struct equiv_cpu_entry *equiv_cpu_table; | 70 | static struct equiv_cpu_entry *equiv_cpu_table; |
86 | 71 | ||
87 | static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) | 72 | static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) |
@@ -144,9 +129,8 @@ static int get_matching_microcode(int cpu, void *mc, int rev) | |||
144 | return 1; | 129 | return 1; |
145 | } | 130 | } |
146 | 131 | ||
147 | static void apply_microcode_amd(int cpu) | 132 | static int apply_microcode_amd(int cpu) |
148 | { | 133 | { |
149 | unsigned long flags; | ||
150 | u32 rev, dummy; | 134 | u32 rev, dummy; |
151 | int cpu_num = raw_smp_processor_id(); | 135 | int cpu_num = raw_smp_processor_id(); |
152 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; | 136 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; |
@@ -156,25 +140,25 @@ static void apply_microcode_amd(int cpu) | |||
156 | BUG_ON(cpu_num != cpu); | 140 | BUG_ON(cpu_num != cpu); |
157 | 141 | ||
158 | if (mc_amd == NULL) | 142 | if (mc_amd == NULL) |
159 | return; | 143 | return 0; |
160 | 144 | ||
161 | spin_lock_irqsave(µcode_update_lock, flags); | ||
162 | wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code); | 145 | wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code); |
163 | /* get patch id after patching */ | 146 | /* get patch id after patching */ |
164 | rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); | 147 | rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); |
165 | spin_unlock_irqrestore(µcode_update_lock, flags); | ||
166 | 148 | ||
167 | /* check current patch id and patch's id for match */ | 149 | /* check current patch id and patch's id for match */ |
168 | if (rev != mc_amd->hdr.patch_id) { | 150 | if (rev != mc_amd->hdr.patch_id) { |
169 | printk(KERN_ERR "microcode: CPU%d: update failed " | 151 | printk(KERN_ERR "microcode: CPU%d: update failed " |
170 | "(for patch_level=0x%x)\n", cpu, mc_amd->hdr.patch_id); | 152 | "(for patch_level=0x%x)\n", cpu, mc_amd->hdr.patch_id); |
171 | return; | 153 | return -1; |
172 | } | 154 | } |
173 | 155 | ||
174 | printk(KERN_INFO "microcode: CPU%d: updated (new patch_level=0x%x)\n", | 156 | printk(KERN_INFO "microcode: CPU%d: updated (new patch_level=0x%x)\n", |
175 | cpu, rev); | 157 | cpu, rev); |
176 | 158 | ||
177 | uci->cpu_sig.rev = rev; | 159 | uci->cpu_sig.rev = rev; |
160 | |||
161 | return 0; | ||
178 | } | 162 | } |
179 | 163 | ||
180 | static int get_ucode_data(void *to, const u8 *from, size_t n) | 164 | static int get_ucode_data(void *to, const u8 *from, size_t n) |
@@ -263,7 +247,8 @@ static void free_equiv_cpu_table(void) | |||
263 | } | 247 | } |
264 | } | 248 | } |
265 | 249 | ||
266 | static int generic_load_microcode(int cpu, const u8 *data, size_t size) | 250 | static enum ucode_state |
251 | generic_load_microcode(int cpu, const u8 *data, size_t size) | ||
267 | { | 252 | { |
268 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 253 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
269 | const u8 *ucode_ptr = data; | 254 | const u8 *ucode_ptr = data; |
@@ -272,12 +257,13 @@ static int generic_load_microcode(int cpu, const u8 *data, size_t size) | |||
272 | int new_rev = uci->cpu_sig.rev; | 257 | int new_rev = uci->cpu_sig.rev; |
273 | unsigned int leftover; | 258 | unsigned int leftover; |
274 | unsigned long offset; | 259 | unsigned long offset; |
260 | enum ucode_state state = UCODE_OK; | ||
275 | 261 | ||
276 | offset = install_equiv_cpu_table(ucode_ptr); | 262 | offset = install_equiv_cpu_table(ucode_ptr); |
277 | if (!offset) { | 263 | if (!offset) { |
278 | printk(KERN_ERR "microcode: failed to create " | 264 | printk(KERN_ERR "microcode: failed to create " |
279 | "equivalent cpu table\n"); | 265 | "equivalent cpu table\n"); |
280 | return -EINVAL; | 266 | return UCODE_ERROR; |
281 | } | 267 | } |
282 | 268 | ||
283 | ucode_ptr += offset; | 269 | ucode_ptr += offset; |
@@ -312,28 +298,27 @@ static int generic_load_microcode(int cpu, const u8 *data, size_t size) | |||
312 | pr_debug("microcode: CPU%d found a matching microcode " | 298 | pr_debug("microcode: CPU%d found a matching microcode " |
313 | "update with version 0x%x (current=0x%x)\n", | 299 | "update with version 0x%x (current=0x%x)\n", |
314 | cpu, new_rev, uci->cpu_sig.rev); | 300 | cpu, new_rev, uci->cpu_sig.rev); |
315 | } else | 301 | } else { |
316 | vfree(new_mc); | 302 | vfree(new_mc); |
317 | } | 303 | state = UCODE_ERROR; |
304 | } | ||
305 | } else | ||
306 | state = UCODE_NFOUND; | ||
318 | 307 | ||
319 | free_equiv_cpu_table(); | 308 | free_equiv_cpu_table(); |
320 | 309 | ||
321 | return (int)leftover; | 310 | return state; |
322 | } | 311 | } |
323 | 312 | ||
324 | static int request_microcode_fw(int cpu, struct device *device) | 313 | static enum ucode_state request_microcode_fw(int cpu, struct device *device) |
325 | { | 314 | { |
326 | const char *fw_name = "amd-ucode/microcode_amd.bin"; | 315 | const char *fw_name = "amd-ucode/microcode_amd.bin"; |
327 | const struct firmware *firmware; | 316 | const struct firmware *firmware; |
328 | int ret; | 317 | enum ucode_state ret; |
329 | |||
330 | /* We should bind the task to the CPU */ | ||
331 | BUG_ON(cpu != raw_smp_processor_id()); | ||
332 | 318 | ||
333 | ret = request_firmware(&firmware, fw_name, device); | 319 | if (request_firmware(&firmware, fw_name, device)) { |
334 | if (ret) { | ||
335 | printk(KERN_ERR "microcode: failed to load file %s\n", fw_name); | 320 | printk(KERN_ERR "microcode: failed to load file %s\n", fw_name); |
336 | return ret; | 321 | return UCODE_NFOUND; |
337 | } | 322 | } |
338 | 323 | ||
339 | ret = generic_load_microcode(cpu, firmware->data, firmware->size); | 324 | ret = generic_load_microcode(cpu, firmware->data, firmware->size); |
@@ -343,11 +328,12 @@ static int request_microcode_fw(int cpu, struct device *device) | |||
343 | return ret; | 328 | return ret; |
344 | } | 329 | } |
345 | 330 | ||
346 | static int request_microcode_user(int cpu, const void __user *buf, size_t size) | 331 | static enum ucode_state |
332 | request_microcode_user(int cpu, const void __user *buf, size_t size) | ||
347 | { | 333 | { |
348 | printk(KERN_INFO "microcode: AMD microcode update via " | 334 | printk(KERN_INFO "microcode: AMD microcode update via " |
349 | "/dev/cpu/microcode not supported\n"); | 335 | "/dev/cpu/microcode not supported\n"); |
350 | return -1; | 336 | return UCODE_ERROR; |
351 | } | 337 | } |
352 | 338 | ||
353 | static void microcode_fini_cpu_amd(int cpu) | 339 | static void microcode_fini_cpu_amd(int cpu) |