Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e5041f3 authored by Yasuaki Ishimatsu's avatar Yasuaki Ishimatsu Committed by Rafael J. Wysocki
Browse files

ACPI / processor: prevent cpu from becoming online



Even if acpi_processor_handle_eject() offlines cpu, there is a chance
to online the cpu after that. So the patch closes the window by using
get/put_online_cpus().

Why does the patch change _cpu_up() logic?

The patch cares the race of hot-remove cpu and _cpu_up(). If the patch
does not change it, there is the following race.

hot-remove cpu                         |  _cpu_up()
------------------------------------- ------------------------------------
call acpi_processor_handle_eject()     |
     call cpu_down()                   |
     call get_online_cpus()            |
                                       | call cpu_hotplug_begin() and stop here
     call arch_unregister_cpu()        |
     call acpi_unmap_lsapic()          |
     call put_online_cpus()            |
                                       | start and continue _cpu_up()
     return acpi_processor_remove()    |
continue hot-remove the cpu            |

So _cpu_up() can continue to itself. And hot-remove cpu can also continue
itself. If the patch changes _cpu_up() logic, the race disappears as below:

hot-remove cpu                         | _cpu_up()
-----------------------------------------------------------------------
call acpi_processor_handle_eject()     |
     call cpu_down()                   |
     call get_online_cpus()            |
                                       | call cpu_hotplug_begin() and stop here
     call arch_unregister_cpu()        |
     call acpi_unmap_lsapic()          |
          cpu's cpu_present is set     |
          to false by set_cpu_present()|
     call put_online_cpus()            |
                                       | start _cpu_up()
                                       | check cpu_present() and return -EINVAL
     return acpi_processor_remove()    |
continue hot-remove the cpu            |

Signed-off-by: default avatarYasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Reviewed-by: default avatarSrivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
Reviewed-by: default avatarToshi Kani <toshi.kani@hp.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 73d4511a
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -852,8 +852,22 @@ static int acpi_processor_handle_eject(struct acpi_processor *pr)
	if (cpu_online(pr->id))
		cpu_down(pr->id);

	get_online_cpus();
	/*
	 * The cpu might become online again at this point. So we check whether
	 * the cpu has been onlined or not. If the cpu became online, it means
	 * that someone wants to use the cpu. So acpi_processor_handle_eject()
	 * returns -EAGAIN.
	 */
	if (unlikely(cpu_online(pr->id))) {
		put_online_cpus();
		pr_warn("Failed to remove CPU %d, because other task "
			"brought the CPU back online\n", pr->id);
		return -EAGAIN;
	}
	arch_unregister_cpu(pr->id);
	acpi_unmap_lsapic(pr->id);
	put_online_cpus();
	return (0);
}
#else
+5 −3
Original line number Diff line number Diff line
@@ -348,11 +348,13 @@ static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen)
	unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
	struct task_struct *idle;

	if (cpu_online(cpu) || !cpu_present(cpu))
		return -EINVAL;

	cpu_hotplug_begin();

	if (cpu_online(cpu) || !cpu_present(cpu)) {
		ret = -EINVAL;
		goto out;
	}

	idle = idle_thread_get(cpu);
	if (IS_ERR(idle)) {
		ret = PTR_ERR(idle);