diff options
Diffstat (limited to 'arch/i386/kernel/microcode.c')
| -rw-r--r-- | arch/i386/kernel/microcode.c | 59 |
1 files changed, 36 insertions, 23 deletions
diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index cbe7ec8dbb9f..83f825f2e2d7 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c | |||
| @@ -567,7 +567,7 @@ static int cpu_request_microcode(int cpu) | |||
| 567 | return error; | 567 | return error; |
| 568 | } | 568 | } |
| 569 | 569 | ||
| 570 | static int apply_microcode_on_cpu(int cpu) | 570 | static int apply_microcode_check_cpu(int cpu) |
| 571 | { | 571 | { |
| 572 | struct cpuinfo_x86 *c = cpu_data + cpu; | 572 | struct cpuinfo_x86 *c = cpu_data + cpu; |
| 573 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 573 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
| @@ -575,8 +575,9 @@ static int apply_microcode_on_cpu(int cpu) | |||
| 575 | unsigned int val[2]; | 575 | unsigned int val[2]; |
| 576 | int err = 0; | 576 | int err = 0; |
| 577 | 577 | ||
| 578 | /* Check if the microcode is available */ | ||
| 578 | if (!uci->mc) | 579 | if (!uci->mc) |
| 579 | return -EINVAL; | 580 | return 0; |
| 580 | 581 | ||
| 581 | old = current->cpus_allowed; | 582 | old = current->cpus_allowed; |
| 582 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); | 583 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); |
| @@ -614,7 +615,7 @@ static int apply_microcode_on_cpu(int cpu) | |||
| 614 | return err; | 615 | return err; |
| 615 | } | 616 | } |
| 616 | 617 | ||
| 617 | static void microcode_init_cpu(int cpu) | 618 | static void microcode_init_cpu(int cpu, int resume) |
| 618 | { | 619 | { |
| 619 | cpumask_t old; | 620 | cpumask_t old; |
| 620 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 621 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
| @@ -624,8 +625,7 @@ static void microcode_init_cpu(int cpu) | |||
| 624 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); | 625 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); |
| 625 | mutex_lock(µcode_mutex); | 626 | mutex_lock(µcode_mutex); |
| 626 | collect_cpu_info(cpu); | 627 | collect_cpu_info(cpu); |
| 627 | if (uci->valid && system_state == SYSTEM_RUNNING && | 628 | if (uci->valid && system_state == SYSTEM_RUNNING && !resume) |
| 628 | !suspend_cpu_hotplug) | ||
| 629 | cpu_request_microcode(cpu); | 629 | cpu_request_microcode(cpu); |
| 630 | mutex_unlock(µcode_mutex); | 630 | mutex_unlock(µcode_mutex); |
| 631 | set_cpus_allowed(current, old); | 631 | set_cpus_allowed(current, old); |
| @@ -702,7 +702,7 @@ static struct attribute_group mc_attr_group = { | |||
| 702 | .name = "microcode", | 702 | .name = "microcode", |
| 703 | }; | 703 | }; |
| 704 | 704 | ||
| 705 | static int mc_sysdev_add(struct sys_device *sys_dev) | 705 | static int __mc_sysdev_add(struct sys_device *sys_dev, int resume) |
| 706 | { | 706 | { |
| 707 | int err, cpu = sys_dev->id; | 707 | int err, cpu = sys_dev->id; |
| 708 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 708 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
| @@ -711,39 +711,31 @@ static int mc_sysdev_add(struct sys_device *sys_dev) | |||
| 711 | return 0; | 711 | return 0; |
| 712 | 712 | ||
| 713 | pr_debug("Microcode:CPU %d added\n", cpu); | 713 | pr_debug("Microcode:CPU %d added\n", cpu); |
| 714 | /* If suspend_cpu_hotplug is set, the system is resuming and we should | 714 | memset(uci, 0, sizeof(*uci)); |
| 715 | * use the data from before the suspend. | ||
| 716 | */ | ||
| 717 | if (suspend_cpu_hotplug) { | ||
| 718 | err = apply_microcode_on_cpu(cpu); | ||
| 719 | if (err) | ||
| 720 | microcode_fini_cpu(cpu); | ||
| 721 | } | ||
| 722 | if (!uci->valid) | ||
| 723 | memset(uci, 0, sizeof(*uci)); | ||
| 724 | 715 | ||
| 725 | err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); | 716 | err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); |
| 726 | if (err) | 717 | if (err) |
| 727 | return err; | 718 | return err; |
| 728 | 719 | ||
| 729 | if (!uci->valid) | 720 | microcode_init_cpu(cpu, resume); |
| 730 | microcode_init_cpu(cpu); | ||
| 731 | 721 | ||
| 732 | return 0; | 722 | return 0; |
| 733 | } | 723 | } |
| 734 | 724 | ||
| 725 | static int mc_sysdev_add(struct sys_device *sys_dev) | ||
| 726 | { | ||
| 727 | return __mc_sysdev_add(sys_dev, 0); | ||
| 728 | } | ||
| 729 | |||
| 735 | static int mc_sysdev_remove(struct sys_device *sys_dev) | 730 | static int mc_sysdev_remove(struct sys_device *sys_dev) |
| 736 | { | 731 | { |
| 737 | int cpu = sys_dev->id; | 732 | int cpu = sys_dev->id; |
| 738 | 733 | ||
| 739 | if (!cpu_online(cpu)) | 734 | if (!cpu_online(cpu)) |
| 740 | return 0; | 735 | return 0; |
| 736 | |||
| 741 | pr_debug("Microcode:CPU %d removed\n", cpu); | 737 | pr_debug("Microcode:CPU %d removed\n", cpu); |
| 742 | /* If suspend_cpu_hotplug is set, the system is suspending and we should | 738 | microcode_fini_cpu(cpu); |
| 743 | * keep the microcode in memory for the resume. | ||
| 744 | */ | ||
| 745 | if (!suspend_cpu_hotplug) | ||
| 746 | microcode_fini_cpu(cpu); | ||
| 747 | sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); | 739 | sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); |
| 748 | return 0; | 740 | return 0; |
| 749 | } | 741 | } |
| @@ -774,13 +766,34 @@ mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) | |||
| 774 | 766 | ||
| 775 | sys_dev = get_cpu_sysdev(cpu); | 767 | sys_dev = get_cpu_sysdev(cpu); |
| 776 | switch (action) { | 768 | switch (action) { |
| 769 | case CPU_UP_CANCELED_FROZEN: | ||
| 770 | /* The CPU refused to come up during a system resume */ | ||
| 771 | microcode_fini_cpu(cpu); | ||
| 772 | break; | ||
| 777 | case CPU_ONLINE: | 773 | case CPU_ONLINE: |
| 778 | case CPU_DOWN_FAILED: | 774 | case CPU_DOWN_FAILED: |
| 779 | mc_sysdev_add(sys_dev); | 775 | mc_sysdev_add(sys_dev); |
| 780 | break; | 776 | break; |
| 777 | case CPU_ONLINE_FROZEN: | ||
| 778 | /* System-wide resume is in progress, try to apply microcode */ | ||
| 779 | if (apply_microcode_check_cpu(cpu)) { | ||
| 780 | /* The application of microcode failed */ | ||
| 781 | microcode_fini_cpu(cpu); | ||
| 782 | __mc_sysdev_add(sys_dev, 1); | ||
| 783 | break; | ||
| 784 | } | ||
| 785 | case CPU_DOWN_FAILED_FROZEN: | ||
| 786 | if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group)) | ||
| 787 | printk(KERN_ERR "Microcode: Failed to create the sysfs " | ||
| 788 | "group for CPU%d\n", cpu); | ||
| 789 | break; | ||
| 781 | case CPU_DOWN_PREPARE: | 790 | case CPU_DOWN_PREPARE: |
| 782 | mc_sysdev_remove(sys_dev); | 791 | mc_sysdev_remove(sys_dev); |
| 783 | break; | 792 | break; |
| 793 | case CPU_DOWN_PREPARE_FROZEN: | ||
| 794 | /* Suspend is in progress, only remove the interface */ | ||
| 795 | sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); | ||
| 796 | break; | ||
| 784 | } | 797 | } |
| 785 | return NOTIFY_OK; | 798 | return NOTIFY_OK; |
| 786 | } | 799 | } |
