Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

[PATCH] Fix microcode-related suspend problem

Fix the regression resulting from the recent change of suspend code
ordering that causes systems based on Intel x86 CPUs using the microcode
driver to hang during the resume.

The problem occurs since the microcode driver uses request_firmware() in
its CPU hotplug notifier, which is called after tasks has been frozen and
hangs. It can be fixed by telling the microcode driver to use the
microcode stored in memory during the resume instead of trying to load it
from disk.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Adrian Bunk <bunk@stusta.de>
Cc: Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Maxim <maximlevitsky@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Rafael J. Wysocki and committed by
Linus Torvalds
1d64b9cb 0c84ce26

+87 -20
+67 -4
arch/i386/kernel/microcode.c
··· 567 567 return error; 568 568 } 569 569 570 + static int apply_microcode_on_cpu(int cpu) 571 + { 572 + struct cpuinfo_x86 *c = cpu_data + cpu; 573 + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 574 + cpumask_t old; 575 + unsigned int val[2]; 576 + int err = 0; 577 + 578 + if (!uci->mc) 579 + return -EINVAL; 580 + 581 + old = current->cpus_allowed; 582 + set_cpus_allowed(current, cpumask_of_cpu(cpu)); 583 + 584 + /* Check if the microcode we have in memory matches the CPU */ 585 + if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || 586 + cpu_has(c, X86_FEATURE_IA64) || uci->sig != cpuid_eax(0x00000001)) 587 + err = -EINVAL; 588 + 589 + if (!err && ((c->x86_model >= 5) || (c->x86 > 6))) { 590 + /* get processor flags from MSR 0x17 */ 591 + rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); 592 + if (uci->pf != (1 << ((val[1] >> 18) & 7))) 593 + err = -EINVAL; 594 + } 595 + 596 + if (!err) { 597 + wrmsr(MSR_IA32_UCODE_REV, 0, 0); 598 + /* see notes above for revision 1.07. Apparent chip bug */ 599 + sync_core(); 600 + /* get the current revision from MSR 0x8B */ 601 + rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); 602 + if (uci->rev != val[1]) 603 + err = -EINVAL; 604 + } 605 + 606 + if (!err) 607 + apply_microcode(cpu); 608 + else 609 + printk(KERN_ERR "microcode: Could not apply microcode to CPU%d:" 610 + " sig=0x%x, pf=0x%x, rev=0x%x\n", 611 + cpu, uci->sig, uci->pf, uci->rev); 612 + 613 + set_cpus_allowed(current, old); 614 + return err; 615 + } 616 + 570 617 static void microcode_init_cpu(int cpu) 571 618 { 572 619 cpumask_t old; ··· 624 577 set_cpus_allowed(current, cpumask_of_cpu(cpu)); 625 578 mutex_lock(&microcode_mutex); 626 579 collect_cpu_info(cpu); 627 - if (uci->valid && system_state == SYSTEM_RUNNING) 580 + if (uci->valid && system_state == SYSTEM_RUNNING && 581 + !suspend_cpu_hotplug) 628 582 cpu_request_microcode(cpu); 629 583 mutex_unlock(&microcode_mutex); 630 584 set_cpus_allowed(current, old); ··· 711 663 return 0; 712 664 713 665 pr_debug("Microcode:CPU %d added\n", cpu); 714 - memset(uci, 0, sizeof(*uci)); 666 + /* If suspend_cpu_hotplug is set, the system is resuming and we should 667 + * use the data from before the suspend. 668 + */ 669 + if (suspend_cpu_hotplug) { 670 + err = apply_microcode_on_cpu(cpu); 671 + if (err) 672 + microcode_fini_cpu(cpu); 673 + } 674 + if (!uci->valid) 675 + memset(uci, 0, sizeof(*uci)); 715 676 716 677 err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); 717 678 if (err) 718 679 return err; 719 680 720 - microcode_init_cpu(cpu); 681 + if (!uci->valid) 682 + microcode_init_cpu(cpu); 683 + 721 684 return 0; 722 685 } 723 686 ··· 739 680 if (!cpu_online(cpu)) 740 681 return 0; 741 682 pr_debug("Microcode:CPU %d removed\n", cpu); 742 - microcode_fini_cpu(cpu); 683 + /* If suspend_cpu_hotplug is set, the system is suspending and we should 684 + * keep the microcode in memory for the resume. 685 + */ 686 + if (!suspend_cpu_hotplug) 687 + microcode_fini_cpu(cpu); 743 688 sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); 744 689 return 0; 745 690 }
+4
include/linux/cpu.h
··· 127 127 #endif /* CONFIG_HOTPLUG_CPU */ 128 128 129 129 #ifdef CONFIG_SUSPEND_SMP 130 + extern int suspend_cpu_hotplug; 131 + 130 132 extern int disable_nonboot_cpus(void); 131 133 extern void enable_nonboot_cpus(void); 132 134 #else 135 + #define suspend_cpu_hotplug 0 136 + 133 137 static inline int disable_nonboot_cpus(void) { return 0; } 134 138 static inline void enable_nonboot_cpus(void) {} 135 139 #endif
+16 -16
kernel/cpu.c
··· 254 254 } 255 255 256 256 #ifdef CONFIG_SUSPEND_SMP 257 + /* Needed to prevent the microcode driver from requesting firmware in its CPU 258 + * hotplug notifier during the suspend/resume. 259 + */ 260 + int suspend_cpu_hotplug; 261 + EXPORT_SYMBOL(suspend_cpu_hotplug); 262 + 257 263 static cpumask_t frozen_cpus; 258 264 259 265 int disable_nonboot_cpus(void) ··· 267 261 int cpu, first_cpu, error = 0; 268 262 269 263 mutex_lock(&cpu_add_remove_lock); 270 - first_cpu = first_cpu(cpu_present_map); 271 - if (!cpu_online(first_cpu)) { 272 - error = _cpu_up(first_cpu); 273 - if (error) { 274 - printk(KERN_ERR "Could not bring CPU%d up.\n", 275 - first_cpu); 276 - goto out; 277 - } 278 - } 279 - 264 + suspend_cpu_hotplug = 1; 265 + first_cpu = first_cpu(cpu_online_map); 280 266 /* We take down all of the non-boot CPUs in one shot to avoid races 281 267 * with the userspace trying to use the CPU hotplug at the same time 282 268 */ ··· 294 296 } else { 295 297 printk(KERN_ERR "Non-boot CPUs are not disabled\n"); 296 298 } 297 - out: 299 + suspend_cpu_hotplug = 0; 298 300 mutex_unlock(&cpu_add_remove_lock); 299 301 return error; 300 302 } ··· 306 308 /* Allow everyone to use the CPU hotplug again */ 307 309 mutex_lock(&cpu_add_remove_lock); 308 310 cpu_hotplug_disabled = 0; 309 - mutex_unlock(&cpu_add_remove_lock); 310 311 if (cpus_empty(frozen_cpus)) 311 - return; 312 + goto out; 312 313 314 + suspend_cpu_hotplug = 1; 313 315 printk("Enabling non-boot CPUs ...\n"); 314 316 for_each_cpu_mask(cpu, frozen_cpus) { 315 - error = cpu_up(cpu); 317 + error = _cpu_up(cpu); 316 318 if (!error) { 317 319 printk("CPU%d is up\n", cpu); 318 320 continue; 319 321 } 320 - printk(KERN_WARNING "Error taking CPU%d up: %d\n", 321 - cpu, error); 322 + printk(KERN_WARNING "Error taking CPU%d up: %d\n", cpu, error); 322 323 } 323 324 cpus_clear(frozen_cpus); 325 + suspend_cpu_hotplug = 0; 326 + out: 327 + mutex_unlock(&cpu_add_remove_lock); 324 328 } 325 329 #endif