mbox series

[v6,00/16] ACPI/arm64: add support for virtual cpu hotplug

Message ID 20240417131909.7925-1-Jonathan.Cameron@huawei.com
Headers show
Series ACPI/arm64: add support for virtual cpu hotplug | expand

Message

Jonathan Cameron April 17, 2024, 1:18 p.m. UTC
Fundamental change: At the level of common ACPI infrastructure, use
the existing hotplug path for arm64 even though what needs to be
done at the architecture specific level is quite different.

An explicit check in arch_register_cpu() for arm64 prevents
this code doing anything if Physical CPU Hotplug is signalled.

This should resolve any concerns about treating virtual CPU
hotplug as if it were physical and potential unwanted side effects
if physical CPU hotplug is added to the ARM architecture in the
future.

v6: Thanks to Rafael for extensive help with the approach + reviews.
Specific changes:
 - Do not differentiate wrt code flow between traditional CPU HP
   and the new ARM flow.  The conditions on performing hotplug actions
   do need to be adjusted though to incorporate the slightly different
   state transition
     Added PRESENT + !ENABLED -> PRESENT + ENABLED
     to existing !PRESENT + !ENABLED -> PRESENT + ENABLED
 - Enable ACPI_HOTPLUG_CPU on arm64 and drop the earlier patches that
   took various code out of the protection of that.  Now the paths
 - New patch to drop unnecessary _STA check in hotplug code. This
   code cannot be entered unless ENABLED + PRESENT are set.
 - New patch to unify the flow of already onlined (at time of driver
   load) and hotplugged CPUs in acpi/processor_driver.c.
   This change is necessary because we can't easily distinguish the
   2 cases of deferred vs hotplug calls of register_cpu() on arm64.
   It is also a nice simplification.
 - Use flags rather than a structure for the extra parameter to
   acpi_scan_check_and_detach() - Thank to Shameer for offline feedback.

Updated version of James' original introduction.

This series adds what looks like cpuhotplug support to arm64 for use in
virtual machines. It does this by moving the cpu_register() calls for
architectures that support ACPI into an arch specific call made from
the ACPI processor driver.
 
The kubernetes folk really want to be able to add CPUs to an existing VM,
in exactly the same way they do on x86. The use-case is pre-booting guests
with one CPU, then adding the number that were actually needed when the
workload is provisioned.

Wait? Doesn't arm64 support cpuhotplug already!?
In the arm world, cpuhotplug gets used to mean removing the power from a CPU.
The CPU is offline, and remains present. For x86, and ACPI, cpuhotplug
has the additional step of physically removing the CPU, so that it isn't
present anymore.
 
Arm64 doesn't support this, and can't support it: CPUs are really a slice
of the SoC, and there is not enough information in the existing ACPI tables
to describe which bits of the slice also got removed. Without a reference
machine: adding this support to the spec is a wild goose chase.
 
Critically: everything described in the firmware tables must remain present.
 
For a virtual machine this is easy as all the other bits of 'virtual SoC'
are emulated, so they can (and do) remain present when a vCPU is 'removed'.

On a system that supports cpuhotplug the MADT has to describe every possible
CPU at boot. Under KVM, the vGIC needs to know about every possible vCPU before
the guest is started.
With these constraints, virtual-cpuhotplug is really just a hypervisor/firmware
policy about which CPUs can be brought online.
 
This series adds support for virtual-cpuhotplug as exactly that: firmware
policy. This may even work on a physical machine too; for a guest the part of
firmware is played by the VMM. (typically Qemu).
 
PSCI support is modified to return 'DENIED' if the CPU can't be brought
online/enabled yet. The CPU object's _STA method's enabled bit is used to
indicate firmware's current disposition. If the CPU has its enabled bit clear,
it will not be registered with sysfs, and attempts to bring it online will
fail. The notifications that _STA has changed its value then work in the same
way as physical hotplug, and firmware can cause the CPU to be registered some
time later, allowing it to be brought online.
 
This creates something that looks like cpuhotplug to user-space and the
kernel beyond arm64 architecture specific code, as the sysfs
files appear and disappear, and the udev notifications look the same.
 
One notable difference is the CPU present mask, which is exposed via sysfs.
Because the CPUs remain present throughout, they can still be seen in that mask.
This value does get used by webbrowsers to estimate the number of CPUs
as the CPU online mask is constantly changed on mobile phones.
 
Linux is tolerant of PSCI returning errors, as its always been allowed to do
that. To avoid confusing OS that can't tolerate this, we needed an additional
bit in the MADT GICC flags. This series copies ACPI_MADT_ONLINE_CAPABLE, which
appears to be for this purpose, but calls it ACPI_MADT_GICC_CPU_CAPABLE as it
has a different bit position in the GICC.
 
This code is unconditionally enabled for all ACPI architectures, though for
now only arm64 will have deferred the cpu_register() calls.

If folk want to play along at home, you'll need a copy of Qemu that supports this.
https://github.com/salil-mehta/qemu.git virt-cpuhp-armv8/rfc-v2

Replace your '-smp' argument with something like:
 | -smp cpus=1,maxcpus=3,cores=3,threads=1,sockets=1
 
 then feed the following to the Qemu montior;
 | (qemu) device_add driver=host-arm-cpu,core-id=1,id=cpu1
 | (qemu) device_del cpu1

James Morse (7):
  ACPI: processor: Register deferred CPUs from acpi_processor_get_info()
  ACPI: Add post_eject to struct acpi_scan_handler for cpu hotplug
  arm64: acpi: Move get_cpu_for_acpi_id() to a header
  irqchip/gic-v3: Don't return errors from gic_acpi_match_gicc()
  irqchip/gic-v3: Add support for ACPI's disabled but 'online capable'
    CPUs
  arm64: document virtual CPU hotplug's expectations
  cpumask: Add enabled cpumask for present CPUs that can be brought
    online

Jean-Philippe Brucker (1):
  arm64: psci: Ignore DENIED CPUs

Jonathan Cameron (8):
  ACPI: processor: Simplify initial onlining to use same path for cold
    and hotplug
  cpu: Do not warn on arch_register_cpu() returning -EPROBE_DEFER
  ACPI: processor: Drop duplicated check on _STA (enabled + present)
  ACPI: processor: Move checks and availability of acpi_processor
    earlier
  ACPI: processor: Add acpi_get_processor_handle() helper
  ACPI: scan: switch to flags for acpi_scan_check_and_detach();
  arm64: arch_register_cpu() variant to check if an ACPI handle is now
    available.
  arm64: Kconfig: Enable hotplug CPU on arm64 if ACPI_PROCESSOR is
    enabled.

 .../ABI/testing/sysfs-devices-system-cpu      |   6 +
 Documentation/arch/arm64/cpu-hotplug.rst      |  79 ++++++++++++
 Documentation/arch/arm64/index.rst            |   1 +
 arch/arm64/Kconfig                            |   1 +
 arch/arm64/include/asm/acpi.h                 |  11 ++
 arch/arm64/kernel/acpi.c                      |  16 +++
 arch/arm64/kernel/acpi_numa.c                 |  11 --
 arch/arm64/kernel/psci.c                      |   2 +-
 arch/arm64/kernel/smp.c                       |  56 ++++++++-
 drivers/acpi/acpi_processor.c                 | 112 +++++++++++-------
 drivers/acpi/processor_driver.c               |  44 ++-----
 drivers/acpi/scan.c                           |  47 ++++++--
 drivers/base/cpu.c                            |  12 +-
 drivers/irqchip/irq-gic-v3.c                  |  32 +++--
 include/acpi/acpi_bus.h                       |   1 +
 include/acpi/processor.h                      |   2 +-
 include/linux/acpi.h                          |  10 +-
 include/linux/cpumask.h                       |  25 ++++
 kernel/cpu.c                                  |   3 +
 19 files changed, 362 insertions(+), 109 deletions(-)
 create mode 100644 Documentation/arch/arm64/cpu-hotplug.rst

Comments

Jonathan Cameron April 17, 2024, 4:55 p.m. UTC | #1
On Wed, 17 Apr 2024 17:33:02 +0100
Salil Mehta <salil.mehta@huawei.com> wrote:

> Hi Jonathan,
> 
> >  From: Jonathan Cameron <jonathan.cameron@huawei.com>
> >  Sent: Wednesday, April 17, 2024 2:19 PM
> >  
> >  The ARM64 architecture does not support physical CPU HP today.
> >  To avoid any possibility of a bug against such an architecture if defined in
> >  future, check for the physical CPU HP case (not present) and return an error
> >  on any such attempt.
> >  
> >  On ARM64 virtual CPU Hotplug relies on the status value that can be queried
> >  via the AML method _STA for the CPU object.
> >  
> >  There are two conditions in which the CPU can be registered.
> >  1) ACPI disabled.
> >  2) ACPI enabled and the acpi_handle is available.
> >     _STA evaluates to the CPU is both enabled and present.
> >     (Note that in absence of the _STA method they are always in this
> >      state).
> >  
> >  If neither of these conditions is met the CPU is not 'yet' ready to be used
> >  and -EPROBE_DEFER is returned.
> >  
> >  Success occurs in the early attempt to register the CPUs if we are booting
> >  with DT (no concept yet of vCPU HP) if not it succeeds for already enabled
> >  CPUs when the ACPI Processor driver attaches to them.  Finally it may
> >  succeed via the CPU Hotplug code indicating that the CPU is now enabled.
> >  
> >  For ACPI if CONFIG_ACPI_PROCESSOR the only path to get to
> >  arch_register_cpu() with that handle set is via
> >  acpi_processor_hot_add_init() which is only called from an ACPI bus scan in
> >  which _STA has already been queried there is no need to repeat it here.
> >  Add a comment to remind us of this in the future.
> >  
> >  Suggested-by: Rafael J. Wysocki <rafael@kernel.org>
> >  Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> >  ---
> >  v6: Add protection again Physical CPU HP to the arch specific code
> >      and don't actually check _STA
> >  
> >  Tested on arm64 with ACPI + DT build and DT only builds, booting with ACPI
> >  and DT as appropriate.
> >  ---
> >   arch/arm64/kernel/smp.c | 53
> >  +++++++++++++++++++++++++++++++++++++++++
> >   1 file changed, 53 insertions(+)
> >  
> >  diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index
> >  dc0e0b3ec2d4..ccb6ad347df9 100644
> >  --- a/arch/arm64/kernel/smp.c
> >  +++ b/arch/arm64/kernel/smp.c
> >  @@ -504,6 +504,59 @@ static int __init smp_cpu_setup(int cpu)  static bool
> >  bootcpu_valid __initdata;  static unsigned int cpu_count = 1;
> >  
> >  +int arch_register_cpu(int cpu)
> >  +{
> >  +	acpi_handle acpi_handle = acpi_get_processor_handle(cpu);
> >  +	struct cpu *c = &per_cpu(cpu_devices, cpu);
> >  +
> >  +	if (!acpi_disabled && !acpi_handle &&
> >  +	    IS_ENABLED(CONFIG_ACPI_HOTPLUG_CPU))
> >  +		return -EPROBE_DEFER;
> >  +
> >  +#ifdef CONFIG_ACPI_HOTPLUG_CPU
> >  +	/* For now block anything that looks like physical CPU Hotplug */
> >  +	if (invalid_logical_cpuid(cpu) || !cpu_present(cpu)) {
> >  +		pr_err_once("Changing CPU present bit is not
> >  supported\n");
> >  +		return -ENODEV;
> >  +	}
> >  +#endif
> >  +
> >  +	/*
> >  +	 * Availability of the acpi handle is sufficient to establish
> >  +	 * that _STA has aleady been checked. No need to recheck here.
> >  +	 */
> >  +	c->hotpluggable = arch_cpu_is_hotpluggable(cpu);
> >  +  
> 
> 
> We would still need 'enabled' bitmask as applications need a way to clearly
> get which processors are enabled and usable in case of ARM64. Otherwise,
> they will end up scanning the entire MAX CPU space to figure out which
> processors have been plugged or unplugged. It is inefficient to bank upon
> errors to detect this and unnecessary to scan again and again.
>            
> +            set_cpu_enabled(cpu, true);   // will need this change
> 
> 
> And its corresponding additions of enabled bitmask along side the present masks.
> 
> I think we had this discussion in Linaro Open Discussions group few years
> back.

Agreed - but if I understand correctly that is  handled in patch 16 -
which introduced the enabled bitmask. I tested that works and it all seems fine.
Done for all architectures in register_cpu() and unregister_cpu() rather
than in arch specific code.

Jonathan


> 
> 
> >  +	return register_cpu(c, cpu);
> >  +}
> >  +
> >  +#ifdef CONFIG_ACPI_HOTPLUG_CPU
> >  +void arch_unregister_cpu(int cpu)
> >  +{
> >  +	acpi_handle acpi_handle = acpi_get_processor_handle(cpu);
> >  +	struct cpu *c = &per_cpu(cpu_devices, cpu);
> >  +	acpi_status status;
> >  +	unsigned long long sta;
> >  +
> >  +	if (!acpi_handle) {
> >  +		pr_err_once("Removing a CPU without associated ACPI
> >  handle\n");
> >  +		return;
> >  +	}
> >  +
> >  +	status = acpi_evaluate_integer(acpi_handle, "_STA", NULL, &sta);
> >  +	if (ACPI_FAILURE(status))
> >  +		return;
> >  +
> >  +	/* For now do not allow anything that looks like physical CPU HP */
> >  +	if (cpu_present(cpu) && !(sta & ACPI_STA_DEVICE_PRESENT)) {
> >  +		pr_err_once("Changing CPU present bit is not
> >  supported\n");
> >  +		return;
> >  +	}
> >  +  
> 
> For the same reasons as above:
> 
> +            set_cpu_enabled(cpu, flase);   // will need this change
> 
> 
> >  +	unregister_cpu(c);
> >  +}
> >  +#endif /* CONFIG_ACPI_HOTPLUG_CPU */
> >  +
> >   #ifdef CONFIG_ACPI
> >   static struct acpi_madt_generic_interrupt cpu_madt_gicc[NR_CPUS];
> >  
> >  --
> >  2.39.2  
>
Salil Mehta April 17, 2024, 5:03 p.m. UTC | #2
>  From: Jonathan Cameron <jonathan.cameron@huawei.com>
>  Sent: Wednesday, April 17, 2024 5:55 PM
>  
>  On Wed, 17 Apr 2024 17:33:02 +0100
>  Salil Mehta <salil.mehta@huawei.com> wrote:
>  
>  > Hi Jonathan,
>  >
>  > >  From: Jonathan Cameron <jonathan.cameron@huawei.com>
>  > >  Sent: Wednesday, April 17, 2024 2:19 PM
>  > >
>  > >  The ARM64 architecture does not support physical CPU HP today.
>  > >  To avoid any possibility of a bug against such an architecture if
>  > > defined in  future, check for the physical CPU HP case (not present)
>  > > and return an error  on any such attempt.
>  > >
>  > >  On ARM64 virtual CPU Hotplug relies on the status value that can be
>  > > queried  via the AML method _STA for the CPU object.
>  > >
>  > >  There are two conditions in which the CPU can be registered.
>  > >  1) ACPI disabled.
>  > >  2) ACPI enabled and the acpi_handle is available.
>  > >     _STA evaluates to the CPU is both enabled and present.
>  > >     (Note that in absence of the _STA method they are always in this
>  > >      state).
>  > >
>  > >  If neither of these conditions is met the CPU is not 'yet' ready to
>  > > be used  and -EPROBE_DEFER is returned.
>  > >
>  > >  Success occurs in the early attempt to register the CPUs if we are
>  > > booting  with DT (no concept yet of vCPU HP) if not it succeeds for
>  > > already enabled  CPUs when the ACPI Processor driver attaches to
>  > > them.  Finally it may  succeed via the CPU Hotplug code indicating that
>  the CPU is now enabled.
>  > >
>  > >  For ACPI if CONFIG_ACPI_PROCESSOR the only path to get to
>  > >  arch_register_cpu() with that handle set is via
>  > >  acpi_processor_hot_add_init() which is only called from an ACPI bus
>  > > scan in  which _STA has already been queried there is no need to repeat
>  it here.
>  > >  Add a comment to remind us of this in the future.
>  > >
>  > >  Suggested-by: Rafael J. Wysocki <rafael@kernel.org>
>  > >  Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
>  > >  ---
>  > >  v6: Add protection again Physical CPU HP to the arch specific code
>  > >      and don't actually check _STA
>  > >
>  > >  Tested on arm64 with ACPI + DT build and DT only builds, booting
>  > > with ACPI  and DT as appropriate.
>  > >  ---
>  > >   arch/arm64/kernel/smp.c | 53
>  > >  +++++++++++++++++++++++++++++++++++++++++
>  > >   1 file changed, 53 insertions(+)
>  > >
>  > >  diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
>  > > index
>  > >  dc0e0b3ec2d4..ccb6ad347df9 100644
>  > >  --- a/arch/arm64/kernel/smp.c
>  > >  +++ b/arch/arm64/kernel/smp.c
>  > >  @@ -504,6 +504,59 @@ static int __init smp_cpu_setup(int cpu)
>  > > static bool  bootcpu_valid __initdata;  static unsigned int
>  > > cpu_count = 1;
>  > >
>  > >  +int arch_register_cpu(int cpu)
>  > >  +{
>  > >  +	acpi_handle acpi_handle = acpi_get_processor_handle(cpu);
>  > >  +	struct cpu *c = &per_cpu(cpu_devices, cpu);
>  > >  +
>  > >  +	if (!acpi_disabled && !acpi_handle &&
>  > >  +	    IS_ENABLED(CONFIG_ACPI_HOTPLUG_CPU))
>  > >  +		return -EPROBE_DEFER;
>  > >  +
>  > >  +#ifdef CONFIG_ACPI_HOTPLUG_CPU
>  > >  +	/* For now block anything that looks like physical CPU Hotplug */
>  > >  +	if (invalid_logical_cpuid(cpu) || !cpu_present(cpu)) {
>  > >  +		pr_err_once("Changing CPU present bit is not
>  > >  supported\n");
>  > >  +		return -ENODEV;
>  > >  +	}
>  > >  +#endif
>  > >  +
>  > >  +	/*
>  > >  +	 * Availability of the acpi handle is sufficient to establish
>  > >  +	 * that _STA has aleady been checked. No need to recheck here.
>  > >  +	 */
>  > >  +	c->hotpluggable = arch_cpu_is_hotpluggable(cpu);
>  > >  +
>  >
>  >
>  > We would still need 'enabled' bitmask as applications need a way to
>  > clearly get which processors are enabled and usable in case of ARM64.
>  > Otherwise, they will end up scanning the entire MAX CPU space to
>  > figure out which processors have been plugged or unplugged. It is
>  > inefficient to bank upon errors to detect this and unnecessary to scan
>  again and again.
>  >
>  > +            set_cpu_enabled(cpu, true);   // will need this change
>  >
>  >
>  > And its corresponding additions of enabled bitmask along side the present
>  masks.
>  >
>  > I think we had this discussion in Linaro Open Discussions group few
>  > years back.
>  
>  Agreed - but if I understand correctly that is  handled in patch 16 - which
>  introduced the enabled bitmask. I tested that works and it all seems fine.
>  Done for all architectures in register_cpu() and unregister_cpu() rather than
>  in arch specific code.


Sorry, I missed that. Yes, this logic is already present in later patches.


Thanks
Salil.


>  
>  Jonathan
>  
>  
>  >
>  >
>  > >  +	return register_cpu(c, cpu);
>  > >  +}
>  > >  +
>  > >  +#ifdef CONFIG_ACPI_HOTPLUG_CPU
>  > >  +void arch_unregister_cpu(int cpu)
>  > >  +{
>  > >  +	acpi_handle acpi_handle = acpi_get_processor_handle(cpu);
>  > >  +	struct cpu *c = &per_cpu(cpu_devices, cpu);
>  > >  +	acpi_status status;
>  > >  +	unsigned long long sta;
>  > >  +
>  > >  +	if (!acpi_handle) {
>  > >  +		pr_err_once("Removing a CPU without associated ACPI
>  > >  handle\n");
>  > >  +		return;
>  > >  +	}
>  > >  +
>  > >  +	status = acpi_evaluate_integer(acpi_handle, "_STA", NULL, &sta);
>  > >  +	if (ACPI_FAILURE(status))
>  > >  +		return;
>  > >  +
>  > >  +	/* For now do not allow anything that looks like physical CPU HP */
>  > >  +	if (cpu_present(cpu) && !(sta & ACPI_STA_DEVICE_PRESENT)) {
>  > >  +		pr_err_once("Changing CPU present bit is not
>  > >  supported\n");
>  > >  +		return;
>  > >  +	}
>  > >  +
>  >
>  > For the same reasons as above:
>  >
>  > +            set_cpu_enabled(cpu, flase);   // will need this change
>  >
>  >
>  > >  +	unregister_cpu(c);
>  > >  +}
>  > >  +#endif /* CONFIG_ACPI_HOTPLUG_CPU */  +
>  > >   #ifdef CONFIG_ACPI
>  > >   static struct acpi_madt_generic_interrupt cpu_madt_gicc[NR_CPUS];
>  > >
>  > >  --
>  > >  2.39.2
>  >