1

Merge branch 'for-next/vcpu-hotplug' into for-next/core

* for-next/vcpu-hotplug: (21 commits)
  : arm64 support for virtual CPU hotplug (ACPI)
  irqchip/gic-v3: Fix 'broken_rdists' unused warning when !SMP and !ACPI
  arm64: Kconfig: Fix dependencies to enable ACPI_HOTPLUG_CPU
  cpumask: Add enabled cpumask for present CPUs that can be brought online
  arm64: document virtual CPU hotplug's expectations
  arm64: Kconfig: Enable hotplug CPU on arm64 if ACPI_PROCESSOR is enabled.
  arm64: arch_register_cpu() variant to check if an ACPI handle is now available.
  arm64: psci: Ignore DENIED CPUs
  irqchip/gic-v3: Add support for ACPI's disabled but 'online capable' CPUs
  irqchip/gic-v3: Don't return errors from gic_acpi_match_gicc()
  arm64: acpi: Harden get_cpu_for_acpi_id() against missing CPU entry
  arm64: acpi: Move get_cpu_for_acpi_id() to a header
  ACPI: Add post_eject to struct acpi_scan_handler for cpu hotplug
  ACPI: scan: switch to flags for acpi_scan_check_and_detach()
  ACPI: processor: Register deferred CPUs from acpi_processor_get_info()
  ACPI: processor: Add acpi_get_processor_handle() helper
  ACPI: processor: Move checks and availability of acpi_processor earlier
  ACPI: processor: Fix memory leaks in error paths of processor_add()
  ACPI: processor: Return an error if acpi_processor_get_info() fails in processor_add()
  ACPI: processor: Drop duplicated check on _STA (enabled + present)
  cpu: Do not warn on arch_register_cpu() returning -EPROBE_DEFER
  ...
This commit is contained in:
Catalin Marinas 2024-07-11 19:10:02 +01:00
commit 4f3a6c4de7
20 changed files with 406 additions and 137 deletions

View File

@ -694,3 +694,9 @@ Description:
(RO) indicates whether or not the kernel directly supports (RO) indicates whether or not the kernel directly supports
modifying the crash elfcorehdr for CPU hot un/plug and/or modifying the crash elfcorehdr for CPU hot un/plug and/or
on/offline changes. on/offline changes.
What: /sys/devices/system/cpu/enabled
Date: Nov 2022
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
Description:
(RO) the list of CPUs that can be brought online.

View File

@ -0,0 +1,79 @@
.. SPDX-License-Identifier: GPL-2.0
.. _cpuhp_index:
====================
CPU Hotplug and ACPI
====================
CPU hotplug in the arm64 world is commonly used to describe the kernel taking
CPUs online/offline using PSCI. This document is about ACPI firmware allowing
CPUs that were not available during boot to be added to the system later.
``possible`` and ``present`` refer to the state of the CPU as seen by linux.
CPU Hotplug on physical systems - CPUs not present at boot
----------------------------------------------------------
Physical systems need to mark a CPU that is ``possible`` but not ``present`` as
being ``present``. An example would be a dual socket machine, where the package
in one of the sockets can be replaced while the system is running.
This is not supported.
In the arm64 world CPUs are not a single device but a slice of the system.
There are no systems that support the physical addition (or removal) of CPUs
while the system is running, and ACPI is not able to sufficiently describe
them.
e.g. New CPUs come with new caches, but the platform's cache toplogy is
described in a static table, the PPTT. How caches are shared between CPUs is
not discoverable, and must be described by firmware.
e.g. The GIC redistributor for each CPU must be accessed by the driver during
boot to discover the system wide supported features. ACPI's MADT GICC
structures can describe a redistributor associated with a disabled CPU, but
can't describe whether the redistributor is accessible, only that it is not
'always on'.
arm64's ACPI tables assume that everything described is ``present``.
CPU Hotplug on virtual systems - CPUs not enabled at boot
---------------------------------------------------------
Virtual systems have the advantage that all the properties the system will
ever have can be described at boot. There are no power-domain considerations
as such devices are emulated.
CPU Hotplug on virtual systems is supported. It is distinct from physical
CPU Hotplug as all resources are described as ``present``, but CPUs may be
marked as disabled by firmware. Only the CPU's online/offline behaviour is
influenced by firmware. An example is where a virtual machine boots with a
single CPU, and additional CPUs are added once a cloud orchestrator deploys
the workload.
For a virtual machine, the VMM (e.g. Qemu) plays the part of firmware.
Virtual hotplug is implemented as a firmware policy affecting which CPUs can be
brought online. Firmware can enforce its policy via PSCI's return codes. e.g.
``DENIED``.
The ACPI tables must describe all the resources of the virtual machine. CPUs
that firmware wishes to disable either from boot (or later) should not be
``enabled`` in the MADT GICC structures, but should have the ``online capable``
bit set, to indicate they can be enabled later. The boot CPU must be marked as
``enabled``. The 'always on' GICR structure must be used to describe the
redistributors.
CPUs described as ``online capable`` but not ``enabled`` can be set to enabled
by the DSDT's Processor object's _STA method. On virtual systems the _STA method
must always report the CPU as ``present``. Changes to the firmware policy can
be notified to the OS via device-check or eject-request.
CPUs described as ``enabled`` in the static table, should not have their _STA
modified dynamically by firmware. Soft-restart features such as kexec will
re-read the static properties of the system from these static tables, and
may malfunction if these no longer describe the running system. Linux will
re-discover the dynamic properties of the system from the _STA method later
during boot.

View File

@ -13,6 +13,7 @@ ARM64 Architecture
asymmetric-32bit asymmetric-32bit
booting booting
cpu-feature-registers cpu-feature-registers
cpu-hotplug
elf_hwcaps elf_hwcaps
hugetlbpage hugetlbpage
kdump kdump

View File

@ -5,6 +5,7 @@ config ARM64
select ACPI_CCA_REQUIRED if ACPI select ACPI_CCA_REQUIRED if ACPI
select ACPI_GENERIC_GSI if ACPI select ACPI_GENERIC_GSI if ACPI
select ACPI_GTDT if ACPI select ACPI_GTDT if ACPI
select ACPI_HOTPLUG_CPU if ACPI_PROCESSOR && HOTPLUG_CPU
select ACPI_IORT if ACPI select ACPI_IORT if ACPI
select ACPI_REDUCED_HARDWARE_ONLY if ACPI select ACPI_REDUCED_HARDWARE_ONLY if ACPI
select ACPI_MCFG if (ACPI && PCI) select ACPI_MCFG if (ACPI && PCI)

View File

@ -119,6 +119,18 @@ static inline u32 get_acpi_id_for_cpu(unsigned int cpu)
return acpi_cpu_get_madt_gicc(cpu)->uid; return acpi_cpu_get_madt_gicc(cpu)->uid;
} }
static inline int get_cpu_for_acpi_id(u32 uid)
{
int cpu;
for (cpu = 0; cpu < nr_cpu_ids; cpu++)
if (acpi_cpu_get_madt_gicc(cpu) &&
uid == get_acpi_id_for_cpu(cpu))
return cpu;
return -EINVAL;
}
static inline void arch_fix_phys_package_id(int num, u32 slot) { } static inline void arch_fix_phys_package_id(int num, u32 slot) { }
void __init acpi_init_cpus(void); void __init acpi_init_cpus(void);
int apei_claim_sea(struct pt_regs *regs); int apei_claim_sea(struct pt_regs *regs);

View File

@ -30,6 +30,7 @@
#include <linux/pgtable.h> #include <linux/pgtable.h>
#include <acpi/ghes.h> #include <acpi/ghes.h>
#include <acpi/processor.h>
#include <asm/cputype.h> #include <asm/cputype.h>
#include <asm/cpu_ops.h> #include <asm/cpu_ops.h>
#include <asm/daifflags.h> #include <asm/daifflags.h>
@ -438,3 +439,24 @@ void arch_reserve_mem_area(acpi_physical_address addr, size_t size)
{ {
memblock_mark_nomap(addr, size); memblock_mark_nomap(addr, size);
} }
#ifdef CONFIG_ACPI_HOTPLUG_CPU
int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 apci_id,
int *pcpu)
{
/* If an error code is passed in this stub can't fix it */
if (*pcpu < 0) {
pr_warn_once("Unable to map CPU to valid ID\n");
return *pcpu;
}
return 0;
}
EXPORT_SYMBOL(acpi_map_cpu);
int acpi_unmap_cpu(int cpu)
{
return 0;
}
EXPORT_SYMBOL(acpi_unmap_cpu);
#endif /* CONFIG_ACPI_HOTPLUG_CPU */

View File

@ -34,17 +34,6 @@ int __init acpi_numa_get_nid(unsigned int cpu)
return acpi_early_node_map[cpu]; return acpi_early_node_map[cpu];
} }
static inline int get_cpu_for_acpi_id(u32 uid)
{
int cpu;
for (cpu = 0; cpu < nr_cpu_ids; cpu++)
if (uid == get_acpi_id_for_cpu(cpu))
return cpu;
return -EINVAL;
}
static int __init acpi_parse_gicc_pxm(union acpi_subtable_headers *header, static int __init acpi_parse_gicc_pxm(union acpi_subtable_headers *header,
const unsigned long end) const unsigned long end)
{ {

View File

@ -40,7 +40,7 @@ static int cpu_psci_cpu_boot(unsigned int cpu)
{ {
phys_addr_t pa_secondary_entry = __pa_symbol(secondary_entry); phys_addr_t pa_secondary_entry = __pa_symbol(secondary_entry);
int err = psci_ops.cpu_on(cpu_logical_map(cpu), pa_secondary_entry); int err = psci_ops.cpu_on(cpu_logical_map(cpu), pa_secondary_entry);
if (err) if (err && err != -EPERM)
pr_err("failed to boot CPU%d (%d)\n", cpu, err); pr_err("failed to boot CPU%d (%d)\n", cpu, err);
return err; return err;

View File

@ -129,7 +129,8 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
/* Now bring the CPU into our world */ /* Now bring the CPU into our world */
ret = boot_secondary(cpu, idle); ret = boot_secondary(cpu, idle);
if (ret) { if (ret) {
pr_err("CPU%u: failed to boot: %d\n", cpu, ret); if (ret != -EPERM)
pr_err("CPU%u: failed to boot: %d\n", cpu, ret);
return ret; return ret;
} }
@ -507,6 +508,59 @@ static int __init smp_cpu_setup(int cpu)
static bool bootcpu_valid __initdata; static bool bootcpu_valid __initdata;
static unsigned int cpu_count = 1; 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);
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;
}
unregister_cpu(c);
}
#endif /* CONFIG_ACPI_HOTPLUG_CPU */
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static struct acpi_madt_generic_interrupt cpu_madt_gicc[NR_CPUS]; static struct acpi_madt_generic_interrupt cpu_madt_gicc[NR_CPUS];
@ -527,7 +581,8 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
{ {
u64 hwid = processor->arm_mpidr; u64 hwid = processor->arm_mpidr;
if (!acpi_gicc_is_usable(processor)) { if (!(processor->flags &
(ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE))) {
pr_debug("skipping disabled CPU entry with 0x%llx MPIDR\n", hwid); pr_debug("skipping disabled CPU entry with 0x%llx MPIDR\n", hwid);
return; return;
} }

View File

@ -35,6 +35,17 @@ EXPORT_PER_CPU_SYMBOL(processors);
struct acpi_processor_errata errata __read_mostly; struct acpi_processor_errata errata __read_mostly;
EXPORT_SYMBOL_GPL(errata); EXPORT_SYMBOL_GPL(errata);
acpi_handle acpi_get_processor_handle(int cpu)
{
struct acpi_processor *pr;
pr = per_cpu(processors, cpu);
if (pr)
return pr->handle;
return NULL;
}
static int acpi_processor_errata_piix4(struct pci_dev *dev) static int acpi_processor_errata_piix4(struct pci_dev *dev)
{ {
u8 value1 = 0; u8 value1 = 0;
@ -183,20 +194,44 @@ static void __init acpi_pcc_cpufreq_init(void) {}
#endif /* CONFIG_X86 */ #endif /* CONFIG_X86 */
/* Initialization */ /* Initialization */
#ifdef CONFIG_ACPI_HOTPLUG_CPU static DEFINE_PER_CPU(void *, processor_device_array);
static int acpi_processor_hotadd_init(struct acpi_processor *pr)
static int acpi_processor_set_per_cpu(struct acpi_processor *pr,
struct acpi_device *device)
{
BUG_ON(pr->id >= nr_cpu_ids);
/*
* Buggy BIOS check.
* ACPI id of processors can be reported wrongly by the BIOS.
* Don't trust it blindly
*/
if (per_cpu(processor_device_array, pr->id) != NULL &&
per_cpu(processor_device_array, pr->id) != device) {
dev_warn(&device->dev,
"BIOS reported wrong ACPI id %d for the processor\n",
pr->id);
return -EINVAL;
}
/*
* processor_device_array is not cleared on errors to allow buggy BIOS
* checks.
*/
per_cpu(processor_device_array, pr->id) = device;
per_cpu(processors, pr->id) = pr;
return 0;
}
#ifdef CONFIG_ACPI_HOTPLUG_CPU
static int acpi_processor_hotadd_init(struct acpi_processor *pr,
struct acpi_device *device)
{ {
unsigned long long sta;
acpi_status status;
int ret; int ret;
if (invalid_phys_cpuid(pr->phys_id)) if (invalid_phys_cpuid(pr->phys_id))
return -ENODEV; return -ENODEV;
status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta);
if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT))
return -ENODEV;
cpu_maps_update_begin(); cpu_maps_update_begin();
cpus_write_lock(); cpus_write_lock();
@ -204,19 +239,26 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr)
if (ret) if (ret)
goto out; goto out;
ret = arch_register_cpu(pr->id); ret = acpi_processor_set_per_cpu(pr, device);
if (ret) { if (ret) {
acpi_unmap_cpu(pr->id); acpi_unmap_cpu(pr->id);
goto out; goto out;
} }
ret = arch_register_cpu(pr->id);
if (ret) {
/* Leave the processor device array in place to detect buggy bios */
per_cpu(processors, pr->id) = NULL;
acpi_unmap_cpu(pr->id);
goto out;
}
/* /*
* CPU got hot-added, but cpu_data is not initialized yet. Set a flag * CPU got hot-added, but cpu_data is not initialized yet. Do
* to delay cpu_idle/throttling initialization and do it when the CPU * cpu_idle/throttling initialization when the CPU gets online for
* gets online for the first time. * the first time.
*/ */
pr_info("CPU%d has been hot-added\n", pr->id); pr_info("CPU%d has been hot-added\n", pr->id);
pr->flags.need_hotplug_init = 1;
out: out:
cpus_write_unlock(); cpus_write_unlock();
@ -224,7 +266,8 @@ out:
return ret; return ret;
} }
#else #else
static inline int acpi_processor_hotadd_init(struct acpi_processor *pr) static inline int acpi_processor_hotadd_init(struct acpi_processor *pr,
struct acpi_device *device)
{ {
return -ENODEV; return -ENODEV;
} }
@ -239,6 +282,7 @@ static int acpi_processor_get_info(struct acpi_device *device)
acpi_status status = AE_OK; acpi_status status = AE_OK;
static int cpu0_initialized; static int cpu0_initialized;
unsigned long long value; unsigned long long value;
int ret;
acpi_processor_errata(); acpi_processor_errata();
@ -315,19 +359,19 @@ static int acpi_processor_get_info(struct acpi_device *device)
} }
/* /*
* Extra Processor objects may be enumerated on MP systems with * This code is not called unless we know the CPU is present and
* less than the max # of CPUs. They should be ignored _iff * enabled. The two paths are:
* they are physically not present. * a) Initially present CPUs on architectures that do not defer
* * their arch_register_cpu() calls until this point.
* NOTE: Even if the processor has a cpuid, it may not be present * b) Hotplugged CPUs (enabled bit in _STA has transitioned from not
* because cpuid <-> apicid mapping is persistent now. * enabled to enabled)
*/ */
if (invalid_logical_cpuid(pr->id) || !cpu_present(pr->id)) { if (!get_cpu_device(pr->id))
int ret = acpi_processor_hotadd_init(pr); ret = acpi_processor_hotadd_init(pr, device);
else
if (ret) ret = acpi_processor_set_per_cpu(pr, device);
return ret; if (ret)
} return ret;
/* /*
* On some boxes several processors use the same processor bus id. * On some boxes several processors use the same processor bus id.
@ -372,8 +416,6 @@ static int acpi_processor_get_info(struct acpi_device *device)
* (cpu_data(cpu)) values, like CPU feature flags, family, model, etc. * (cpu_data(cpu)) values, like CPU feature flags, family, model, etc.
* Such things have to be put in and set up by the processor driver's .probe(). * Such things have to be put in and set up by the processor driver's .probe().
*/ */
static DEFINE_PER_CPU(void *, processor_device_array);
static int acpi_processor_add(struct acpi_device *device, static int acpi_processor_add(struct acpi_device *device,
const struct acpi_device_id *id) const struct acpi_device_id *id)
{ {
@ -400,39 +442,17 @@ static int acpi_processor_add(struct acpi_device *device,
result = acpi_processor_get_info(device); result = acpi_processor_get_info(device);
if (result) /* Processor is not physically present or unavailable */ if (result) /* Processor is not physically present or unavailable */
return 0; goto err_clear_driver_data;
BUG_ON(pr->id >= nr_cpu_ids);
/*
* Buggy BIOS check.
* ACPI id of processors can be reported wrongly by the BIOS.
* Don't trust it blindly
*/
if (per_cpu(processor_device_array, pr->id) != NULL &&
per_cpu(processor_device_array, pr->id) != device) {
dev_warn(&device->dev,
"BIOS reported wrong ACPI id %d for the processor\n",
pr->id);
/* Give up, but do not abort the namespace scan. */
goto err;
}
/*
* processor_device_array is not cleared on errors to allow buggy BIOS
* checks.
*/
per_cpu(processor_device_array, pr->id) = device;
per_cpu(processors, pr->id) = pr;
dev = get_cpu_device(pr->id); dev = get_cpu_device(pr->id);
if (!dev) { if (!dev) {
result = -ENODEV; result = -ENODEV;
goto err; goto err_clear_per_cpu;
} }
result = acpi_bind_one(dev, device); result = acpi_bind_one(dev, device);
if (result) if (result)
goto err; goto err_clear_per_cpu;
pr->dev = dev; pr->dev = dev;
@ -443,10 +463,11 @@ static int acpi_processor_add(struct acpi_device *device,
dev_err(dev, "Processor driver could not be attached\n"); dev_err(dev, "Processor driver could not be attached\n");
acpi_unbind_one(dev); acpi_unbind_one(dev);
err: err_clear_per_cpu:
free_cpumask_var(pr->throttling.shared_cpu_map);
device->driver_data = NULL;
per_cpu(processors, pr->id) = NULL; per_cpu(processors, pr->id) = NULL;
err_clear_driver_data:
device->driver_data = NULL;
free_cpumask_var(pr->throttling.shared_cpu_map);
err_free_pr: err_free_pr:
kfree(pr); kfree(pr);
return result; return result;
@ -454,7 +475,7 @@ static int acpi_processor_add(struct acpi_device *device,
#ifdef CONFIG_ACPI_HOTPLUG_CPU #ifdef CONFIG_ACPI_HOTPLUG_CPU
/* Removal */ /* Removal */
static void acpi_processor_remove(struct acpi_device *device) static void acpi_processor_post_eject(struct acpi_device *device)
{ {
struct acpi_processor *pr; struct acpi_processor *pr;
@ -476,10 +497,6 @@ static void acpi_processor_remove(struct acpi_device *device)
device_release_driver(pr->dev); device_release_driver(pr->dev);
acpi_unbind_one(pr->dev); acpi_unbind_one(pr->dev);
/* Clean up. */
per_cpu(processor_device_array, pr->id) = NULL;
per_cpu(processors, pr->id) = NULL;
cpu_maps_update_begin(); cpu_maps_update_begin();
cpus_write_lock(); cpus_write_lock();
@ -487,6 +504,10 @@ static void acpi_processor_remove(struct acpi_device *device)
arch_unregister_cpu(pr->id); arch_unregister_cpu(pr->id);
acpi_unmap_cpu(pr->id); acpi_unmap_cpu(pr->id);
/* Clean up. */
per_cpu(processor_device_array, pr->id) = NULL;
per_cpu(processors, pr->id) = NULL;
cpus_write_unlock(); cpus_write_unlock();
cpu_maps_update_done(); cpu_maps_update_done();
@ -622,7 +643,7 @@ static struct acpi_scan_handler processor_handler = {
.ids = processor_device_ids, .ids = processor_device_ids,
.attach = acpi_processor_add, .attach = acpi_processor_add,
#ifdef CONFIG_ACPI_HOTPLUG_CPU #ifdef CONFIG_ACPI_HOTPLUG_CPU
.detach = acpi_processor_remove, .post_eject = acpi_processor_post_eject,
#endif #endif
.hotplug = { .hotplug = {
.enabled = true, .enabled = true,

View File

@ -90,7 +90,8 @@ static int map_gicc_mpidr(struct acpi_subtable_header *entry,
struct acpi_madt_generic_interrupt *gicc = struct acpi_madt_generic_interrupt *gicc =
container_of(entry, struct acpi_madt_generic_interrupt, header); container_of(entry, struct acpi_madt_generic_interrupt, header);
if (!acpi_gicc_is_usable(gicc)) if (!(gicc->flags &
(ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
return -ENODEV; return -ENODEV;
/* device_declaration means Device object in DSDT, in the /* device_declaration means Device object in DSDT, in the

View File

@ -33,7 +33,6 @@ MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_DESCRIPTION("ACPI Processor Driver"); MODULE_DESCRIPTION("ACPI Processor Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static int acpi_processor_start(struct device *dev);
static int acpi_processor_stop(struct device *dev); static int acpi_processor_stop(struct device *dev);
static const struct acpi_device_id processor_device_ids[] = { static const struct acpi_device_id processor_device_ids[] = {
@ -47,7 +46,6 @@ static struct device_driver acpi_processor_driver = {
.name = "processor", .name = "processor",
.bus = &cpu_subsys, .bus = &cpu_subsys,
.acpi_match_table = processor_device_ids, .acpi_match_table = processor_device_ids,
.probe = acpi_processor_start,
.remove = acpi_processor_stop, .remove = acpi_processor_stop,
}; };
@ -115,12 +113,9 @@ static int acpi_soft_cpu_online(unsigned int cpu)
* CPU got physically hotplugged and onlined for the first time: * CPU got physically hotplugged and onlined for the first time:
* Initialize missing things. * Initialize missing things.
*/ */
if (pr->flags.need_hotplug_init) { if (!pr->flags.previously_online) {
int ret; int ret;
pr_info("Will online and init hotplugged CPU: %d\n",
pr->id);
pr->flags.need_hotplug_init = 0;
ret = __acpi_processor_start(device); ret = __acpi_processor_start(device);
WARN(ret, "Failed to start CPU: %d\n", pr->id); WARN(ret, "Failed to start CPU: %d\n", pr->id);
} else { } else {
@ -167,9 +162,6 @@ static int __acpi_processor_start(struct acpi_device *device)
if (!pr) if (!pr)
return -ENODEV; return -ENODEV;
if (pr->flags.need_hotplug_init)
return 0;
result = acpi_cppc_processor_probe(pr); result = acpi_cppc_processor_probe(pr);
if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS)) if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
dev_dbg(&device->dev, "CPPC data invalid or not present\n"); dev_dbg(&device->dev, "CPPC data invalid or not present\n");
@ -185,32 +177,21 @@ static int __acpi_processor_start(struct acpi_device *device)
status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
acpi_processor_notify, device); acpi_processor_notify, device);
if (ACPI_SUCCESS(status)) if (!ACPI_SUCCESS(status)) {
return 0; result = -ENODEV;
goto err_thermal_exit;
}
pr->flags.previously_online = 1;
result = -ENODEV; return 0;
err_thermal_exit:
acpi_processor_thermal_exit(pr, device); acpi_processor_thermal_exit(pr, device);
err_power_exit: err_power_exit:
acpi_processor_power_exit(pr); acpi_processor_power_exit(pr);
return result; return result;
} }
static int acpi_processor_start(struct device *dev)
{
struct acpi_device *device = ACPI_COMPANION(dev);
int ret;
if (!device)
return -ENODEV;
/* Protect against concurrent CPU hotplug operations */
cpu_hotplug_disable();
ret = __acpi_processor_start(device);
cpu_hotplug_enable();
return ret;
}
static int acpi_processor_stop(struct device *dev) static int acpi_processor_stop(struct device *dev)
{ {
struct acpi_device *device = ACPI_COMPANION(dev); struct acpi_device *device = ACPI_COMPANION(dev);
@ -279,9 +260,9 @@ static int __init acpi_processor_driver_init(void)
if (result < 0) if (result < 0)
return result; return result;
result = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, result = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"acpi/cpu-drv:online", "acpi/cpu-drv:online",
acpi_soft_cpu_online, NULL); acpi_soft_cpu_online, NULL);
if (result < 0) if (result < 0)
goto err; goto err;
hp_online = result; hp_online = result;

View File

@ -243,13 +243,17 @@ static int acpi_scan_try_to_offline(struct acpi_device *device)
return 0; return 0;
} }
static int acpi_scan_check_and_detach(struct acpi_device *adev, void *check) #define ACPI_SCAN_CHECK_FLAG_STATUS BIT(0)
#define ACPI_SCAN_CHECK_FLAG_EJECT BIT(1)
static int acpi_scan_check_and_detach(struct acpi_device *adev, void *p)
{ {
struct acpi_scan_handler *handler = adev->handler; struct acpi_scan_handler *handler = adev->handler;
uintptr_t flags = (uintptr_t)p;
acpi_dev_for_each_child_reverse(adev, acpi_scan_check_and_detach, check); acpi_dev_for_each_child_reverse(adev, acpi_scan_check_and_detach, p);
if (check) { if (flags & ACPI_SCAN_CHECK_FLAG_STATUS) {
acpi_bus_get_status(adev); acpi_bus_get_status(adev);
/* /*
* Skip devices that are still there and take the enabled * Skip devices that are still there and take the enabled
@ -269,8 +273,6 @@ static int acpi_scan_check_and_detach(struct acpi_device *adev, void *check)
if (handler) { if (handler) {
if (handler->detach) if (handler->detach)
handler->detach(adev); handler->detach(adev);
adev->handler = NULL;
} else { } else {
device_release_driver(&adev->dev); device_release_driver(&adev->dev);
} }
@ -280,6 +282,28 @@ static int acpi_scan_check_and_detach(struct acpi_device *adev, void *check)
*/ */
acpi_device_set_power(adev, ACPI_STATE_D3_COLD); acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
adev->flags.initialized = false; adev->flags.initialized = false;
/* For eject this is deferred to acpi_bus_post_eject() */
if (!(flags & ACPI_SCAN_CHECK_FLAG_EJECT)) {
adev->handler = NULL;
acpi_device_clear_enumerated(adev);
}
return 0;
}
static int acpi_bus_post_eject(struct acpi_device *adev, void *not_used)
{
struct acpi_scan_handler *handler = adev->handler;
acpi_dev_for_each_child_reverse(adev, acpi_bus_post_eject, NULL);
if (handler) {
if (handler->post_eject)
handler->post_eject(adev);
adev->handler = NULL;
}
acpi_device_clear_enumerated(adev); acpi_device_clear_enumerated(adev);
return 0; return 0;
@ -287,7 +311,9 @@ static int acpi_scan_check_and_detach(struct acpi_device *adev, void *check)
static void acpi_scan_check_subtree(struct acpi_device *adev) static void acpi_scan_check_subtree(struct acpi_device *adev)
{ {
acpi_scan_check_and_detach(adev, (void *)true); uintptr_t flags = ACPI_SCAN_CHECK_FLAG_STATUS;
acpi_scan_check_and_detach(adev, (void *)flags);
} }
static int acpi_scan_hot_remove(struct acpi_device *device) static int acpi_scan_hot_remove(struct acpi_device *device)
@ -295,6 +321,7 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
acpi_handle handle = device->handle; acpi_handle handle = device->handle;
unsigned long long sta; unsigned long long sta;
acpi_status status; acpi_status status;
uintptr_t flags = ACPI_SCAN_CHECK_FLAG_EJECT;
if (device->handler && device->handler->hotplug.demand_offline) { if (device->handler && device->handler->hotplug.demand_offline) {
if (!acpi_scan_is_offline(device, true)) if (!acpi_scan_is_offline(device, true))
@ -307,7 +334,7 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
acpi_handle_debug(handle, "Ejecting\n"); acpi_handle_debug(handle, "Ejecting\n");
acpi_bus_trim(device); acpi_scan_check_and_detach(device, (void *)flags);
acpi_evaluate_lck(handle, 0); acpi_evaluate_lck(handle, 0);
/* /*
@ -330,6 +357,8 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
} else if (sta & ACPI_STA_DEVICE_ENABLED) { } else if (sta & ACPI_STA_DEVICE_ENABLED) {
acpi_handle_warn(handle, acpi_handle_warn(handle,
"Eject incomplete - status 0x%llx\n", sta); "Eject incomplete - status 0x%llx\n", sta);
} else {
acpi_bus_post_eject(device, NULL);
} }
return 0; return 0;
@ -2596,7 +2625,9 @@ EXPORT_SYMBOL(acpi_bus_scan);
*/ */
void acpi_bus_trim(struct acpi_device *adev) void acpi_bus_trim(struct acpi_device *adev)
{ {
acpi_scan_check_and_detach(adev, NULL); uintptr_t flags = 0;
acpi_scan_check_and_detach(adev, (void *)flags);
} }
EXPORT_SYMBOL_GPL(acpi_bus_trim); EXPORT_SYMBOL_GPL(acpi_bus_trim);

View File

@ -95,6 +95,7 @@ void unregister_cpu(struct cpu *cpu)
{ {
int logical_cpu = cpu->dev.id; int logical_cpu = cpu->dev.id;
set_cpu_enabled(logical_cpu, false);
unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu)); unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
device_unregister(&cpu->dev); device_unregister(&cpu->dev);
@ -273,6 +274,13 @@ static ssize_t print_cpus_offline(struct device *dev,
} }
static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL); static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL);
static ssize_t print_cpus_enabled(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpu_enabled_mask));
}
static DEVICE_ATTR(enabled, 0444, print_cpus_enabled, NULL);
static ssize_t print_cpus_isolated(struct device *dev, static ssize_t print_cpus_isolated(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@ -413,6 +421,7 @@ int register_cpu(struct cpu *cpu, int num)
register_cpu_under_node(num, cpu_to_node(num)); register_cpu_under_node(num, cpu_to_node(num));
dev_pm_qos_expose_latency_limit(&cpu->dev, dev_pm_qos_expose_latency_limit(&cpu->dev,
PM_QOS_RESUME_LATENCY_NO_CONSTRAINT); PM_QOS_RESUME_LATENCY_NO_CONSTRAINT);
set_cpu_enabled(num, true);
return 0; return 0;
} }
@ -494,6 +503,7 @@ static struct attribute *cpu_root_attrs[] = {
&cpu_attrs[2].attr.attr, &cpu_attrs[2].attr.attr,
&dev_attr_kernel_max.attr, &dev_attr_kernel_max.attr,
&dev_attr_offline.attr, &dev_attr_offline.attr,
&dev_attr_enabled.attr,
&dev_attr_isolated.attr, &dev_attr_isolated.attr,
#ifdef CONFIG_NO_HZ_FULL #ifdef CONFIG_NO_HZ_FULL
&dev_attr_nohz_full.attr, &dev_attr_nohz_full.attr,
@ -558,7 +568,7 @@ static void __init cpu_dev_register_generic(void)
for_each_present_cpu(i) { for_each_present_cpu(i) {
ret = arch_register_cpu(i); ret = arch_register_cpu(i);
if (ret) if (ret && ret != -EPROBE_DEFER)
pr_warn("register_cpu %d failed (%d)\n", i, ret); pr_warn("register_cpu %d failed (%d)\n", i, ret);
} }
} }

View File

@ -47,6 +47,8 @@ static u8 dist_prio_nmi __ro_after_init = GICV3_PRIO_NMI;
#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1) #define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1)
static struct cpumask broken_rdists __read_mostly __maybe_unused;
struct redist_region { struct redist_region {
void __iomem *redist_base; void __iomem *redist_base;
phys_addr_t phys_base; phys_addr_t phys_base;
@ -1317,6 +1319,18 @@ static void gic_cpu_init(void)
#define MPIDR_TO_SGI_RS(mpidr) (MPIDR_RS(mpidr) << ICC_SGI1R_RS_SHIFT) #define MPIDR_TO_SGI_RS(mpidr) (MPIDR_RS(mpidr) << ICC_SGI1R_RS_SHIFT)
#define MPIDR_TO_SGI_CLUSTER_ID(mpidr) ((mpidr) & ~0xFUL) #define MPIDR_TO_SGI_CLUSTER_ID(mpidr) ((mpidr) & ~0xFUL)
/*
* gic_starting_cpu() is called after the last point where cpuhp is allowed
* to fail. So pre check for problems earlier.
*/
static int gic_check_rdist(unsigned int cpu)
{
if (cpumask_test_cpu(cpu, &broken_rdists))
return -EINVAL;
return 0;
}
static int gic_starting_cpu(unsigned int cpu) static int gic_starting_cpu(unsigned int cpu)
{ {
gic_cpu_init(); gic_cpu_init();
@ -1408,6 +1422,10 @@ static void __init gic_smp_init(void)
}; };
int base_sgi; int base_sgi;
cpuhp_setup_state_nocalls(CPUHP_BP_PREPARE_DYN,
"irqchip/arm/gicv3:checkrdist",
gic_check_rdist, NULL);
cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING, cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
"irqchip/arm/gicv3:starting", "irqchip/arm/gicv3:starting",
gic_starting_cpu, NULL); gic_starting_cpu, NULL);
@ -2360,9 +2378,25 @@ gic_acpi_parse_madt_gicc(union acpi_subtable_headers *header,
u32 size = reg == GIC_PIDR2_ARCH_GICv4 ? SZ_64K * 4 : SZ_64K * 2; u32 size = reg == GIC_PIDR2_ARCH_GICv4 ? SZ_64K * 4 : SZ_64K * 2;
void __iomem *redist_base; void __iomem *redist_base;
if (!acpi_gicc_is_usable(gicc)) /* Neither enabled or online capable means it doesn't exist, skip it */
if (!(gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
return 0; return 0;
/*
* Capable but disabled CPUs can be brought online later. What about
* the redistributor? ACPI doesn't want to say!
* Virtual hotplug systems can use the MADT's "always-on" GICR entries.
* Otherwise, prevent such CPUs from being brought online.
*/
if (!(gicc->flags & ACPI_MADT_ENABLED)) {
int cpu = get_cpu_for_acpi_id(gicc->uid);
pr_warn("CPU %u's redistributor is inaccessible: this CPU can't be brought online\n", cpu);
if (cpu >= 0)
cpumask_set_cpu(cpu, &broken_rdists);
return 0;
}
redist_base = ioremap(gicc->gicr_base_address, size); redist_base = ioremap(gicc->gicr_base_address, size);
if (!redist_base) if (!redist_base)
return -ENOMEM; return -ENOMEM;
@ -2408,21 +2442,15 @@ static int __init gic_acpi_match_gicc(union acpi_subtable_headers *header,
/* /*
* If GICC is enabled and has valid gicr base address, then it means * If GICC is enabled and has valid gicr base address, then it means
* GICR base is presented via GICC * GICR base is presented via GICC. The redistributor is only known to
* be accessible if the GICC is marked as enabled. If this bit is not
* set, we'd need to add the redistributor at runtime, which isn't
* supported.
*/ */
if (acpi_gicc_is_usable(gicc) && gicc->gicr_base_address) { if (gicc->flags & ACPI_MADT_ENABLED && gicc->gicr_base_address)
acpi_data.enabled_rdists++; acpi_data.enabled_rdists++;
return 0;
}
/* return 0;
* It's perfectly valid firmware can pass disabled GICC entry, driver
* should not treat as errors, skip the entry instead of probe fail.
*/
if (!acpi_gicc_is_usable(gicc))
return 0;
return -ENODEV;
} }
static int __init gic_acpi_count_gicr_regions(void) static int __init gic_acpi_count_gicr_regions(void)
@ -2478,7 +2506,8 @@ static int __init gic_acpi_parse_virt_madt_gicc(union acpi_subtable_headers *hea
int maint_irq_mode; int maint_irq_mode;
static int first_madt = true; static int first_madt = true;
if (!acpi_gicc_is_usable(gicc)) if (!(gicc->flags &
(ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
return 0; return 0;
maint_irq_mode = (gicc->flags & ACPI_MADT_VGIC_IRQ_MODE) ? maint_irq_mode = (gicc->flags & ACPI_MADT_VGIC_IRQ_MODE) ?

View File

@ -134,6 +134,7 @@ struct acpi_scan_handler {
bool (*match)(const char *idstr, const struct acpi_device_id **matchid); bool (*match)(const char *idstr, const struct acpi_device_id **matchid);
int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id);
void (*detach)(struct acpi_device *dev); void (*detach)(struct acpi_device *dev);
void (*post_eject)(struct acpi_device *dev);
void (*bind)(struct device *phys_dev); void (*bind)(struct device *phys_dev);
void (*unbind)(struct device *phys_dev); void (*unbind)(struct device *phys_dev);
struct acpi_hotplug_profile hotplug; struct acpi_hotplug_profile hotplug;

View File

@ -217,7 +217,7 @@ struct acpi_processor_flags {
u8 has_lpi:1; u8 has_lpi:1;
u8 power_setup_done:1; u8 power_setup_done:1;
u8 bm_rld_set:1; u8 bm_rld_set:1;
u8 need_hotplug_init:1; u8 previously_online:1;
}; };
struct acpi_processor { struct acpi_processor {

View File

@ -237,11 +237,6 @@ acpi_table_parse_cedt(enum acpi_cedt_type id,
int acpi_parse_mcfg (struct acpi_table_header *header); int acpi_parse_mcfg (struct acpi_table_header *header);
void acpi_table_print_madt_entry (struct acpi_subtable_header *madt); void acpi_table_print_madt_entry (struct acpi_subtable_header *madt);
static inline bool acpi_gicc_is_usable(struct acpi_madt_generic_interrupt *gicc)
{
return gicc->flags & ACPI_MADT_ENABLED;
}
#if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH) #if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH)
void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa); void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa);
#else #else
@ -304,6 +299,8 @@ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id,
int acpi_unmap_cpu(int cpu); int acpi_unmap_cpu(int cpu);
#endif /* CONFIG_ACPI_HOTPLUG_CPU */ #endif /* CONFIG_ACPI_HOTPLUG_CPU */
acpi_handle acpi_get_processor_handle(int cpu);
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr); int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr);
#endif #endif
@ -1076,6 +1073,11 @@ static inline bool acpi_sleep_state_supported(u8 sleep_state)
return false; return false;
} }
static inline acpi_handle acpi_get_processor_handle(int cpu)
{
return NULL;
}
#endif /* !CONFIG_ACPI */ #endif /* !CONFIG_ACPI */
extern void arch_post_acpi_subsys_init(void); extern void arch_post_acpi_subsys_init(void);

View File

@ -93,6 +93,7 @@ static inline void set_nr_cpu_ids(unsigned int nr)
* *
* cpu_possible_mask- has bit 'cpu' set iff cpu is populatable * cpu_possible_mask- has bit 'cpu' set iff cpu is populatable
* cpu_present_mask - has bit 'cpu' set iff cpu is populated * cpu_present_mask - has bit 'cpu' set iff cpu is populated
* cpu_enabled_mask - has bit 'cpu' set iff cpu can be brought online
* cpu_online_mask - has bit 'cpu' set iff cpu available to scheduler * cpu_online_mask - has bit 'cpu' set iff cpu available to scheduler
* cpu_active_mask - has bit 'cpu' set iff cpu available to migration * cpu_active_mask - has bit 'cpu' set iff cpu available to migration
* *
@ -125,11 +126,13 @@ static inline void set_nr_cpu_ids(unsigned int nr)
extern struct cpumask __cpu_possible_mask; extern struct cpumask __cpu_possible_mask;
extern struct cpumask __cpu_online_mask; extern struct cpumask __cpu_online_mask;
extern struct cpumask __cpu_enabled_mask;
extern struct cpumask __cpu_present_mask; extern struct cpumask __cpu_present_mask;
extern struct cpumask __cpu_active_mask; extern struct cpumask __cpu_active_mask;
extern struct cpumask __cpu_dying_mask; extern struct cpumask __cpu_dying_mask;
#define cpu_possible_mask ((const struct cpumask *)&__cpu_possible_mask) #define cpu_possible_mask ((const struct cpumask *)&__cpu_possible_mask)
#define cpu_online_mask ((const struct cpumask *)&__cpu_online_mask) #define cpu_online_mask ((const struct cpumask *)&__cpu_online_mask)
#define cpu_enabled_mask ((const struct cpumask *)&__cpu_enabled_mask)
#define cpu_present_mask ((const struct cpumask *)&__cpu_present_mask) #define cpu_present_mask ((const struct cpumask *)&__cpu_present_mask)
#define cpu_active_mask ((const struct cpumask *)&__cpu_active_mask) #define cpu_active_mask ((const struct cpumask *)&__cpu_active_mask)
#define cpu_dying_mask ((const struct cpumask *)&__cpu_dying_mask) #define cpu_dying_mask ((const struct cpumask *)&__cpu_dying_mask)
@ -1075,6 +1078,7 @@ extern const DECLARE_BITMAP(cpu_all_bits, NR_CPUS);
#else #else
#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask) #define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
#define for_each_online_cpu(cpu) for_each_cpu((cpu), cpu_online_mask) #define for_each_online_cpu(cpu) for_each_cpu((cpu), cpu_online_mask)
#define for_each_enabled_cpu(cpu) for_each_cpu((cpu), cpu_enabled_mask)
#define for_each_present_cpu(cpu) for_each_cpu((cpu), cpu_present_mask) #define for_each_present_cpu(cpu) for_each_cpu((cpu), cpu_present_mask)
#endif #endif
@ -1092,6 +1096,15 @@ set_cpu_possible(unsigned int cpu, bool possible)
cpumask_clear_cpu(cpu, &__cpu_possible_mask); cpumask_clear_cpu(cpu, &__cpu_possible_mask);
} }
static inline void
set_cpu_enabled(unsigned int cpu, bool can_be_onlined)
{
if (can_be_onlined)
cpumask_set_cpu(cpu, &__cpu_enabled_mask);
else
cpumask_clear_cpu(cpu, &__cpu_enabled_mask);
}
static inline void static inline void
set_cpu_present(unsigned int cpu, bool present) set_cpu_present(unsigned int cpu, bool present)
{ {
@ -1173,6 +1186,7 @@ static __always_inline unsigned int num_online_cpus(void)
return raw_atomic_read(&__num_online_cpus); return raw_atomic_read(&__num_online_cpus);
} }
#define num_possible_cpus() cpumask_weight(cpu_possible_mask) #define num_possible_cpus() cpumask_weight(cpu_possible_mask)
#define num_enabled_cpus() cpumask_weight(cpu_enabled_mask)
#define num_present_cpus() cpumask_weight(cpu_present_mask) #define num_present_cpus() cpumask_weight(cpu_present_mask)
#define num_active_cpus() cpumask_weight(cpu_active_mask) #define num_active_cpus() cpumask_weight(cpu_active_mask)
@ -1181,6 +1195,11 @@ static inline bool cpu_online(unsigned int cpu)
return cpumask_test_cpu(cpu, cpu_online_mask); return cpumask_test_cpu(cpu, cpu_online_mask);
} }
static inline bool cpu_enabled(unsigned int cpu)
{
return cpumask_test_cpu(cpu, cpu_enabled_mask);
}
static inline bool cpu_possible(unsigned int cpu) static inline bool cpu_possible(unsigned int cpu)
{ {
return cpumask_test_cpu(cpu, cpu_possible_mask); return cpumask_test_cpu(cpu, cpu_possible_mask);
@ -1205,6 +1224,7 @@ static inline bool cpu_dying(unsigned int cpu)
#define num_online_cpus() 1U #define num_online_cpus() 1U
#define num_possible_cpus() 1U #define num_possible_cpus() 1U
#define num_enabled_cpus() 1U
#define num_present_cpus() 1U #define num_present_cpus() 1U
#define num_active_cpus() 1U #define num_active_cpus() 1U
@ -1218,6 +1238,11 @@ static inline bool cpu_possible(unsigned int cpu)
return cpu == 0; return cpu == 0;
} }
static inline bool cpu_enabled(unsigned int cpu)
{
return cpu == 0;
}
static inline bool cpu_present(unsigned int cpu) static inline bool cpu_present(unsigned int cpu)
{ {
return cpu == 0; return cpu == 0;

View File

@ -3069,6 +3069,9 @@ EXPORT_SYMBOL(__cpu_possible_mask);
struct cpumask __cpu_online_mask __read_mostly; struct cpumask __cpu_online_mask __read_mostly;
EXPORT_SYMBOL(__cpu_online_mask); EXPORT_SYMBOL(__cpu_online_mask);
struct cpumask __cpu_enabled_mask __read_mostly;
EXPORT_SYMBOL(__cpu_enabled_mask);
struct cpumask __cpu_present_mask __read_mostly; struct cpumask __cpu_present_mask __read_mostly;
EXPORT_SYMBOL(__cpu_present_mask); EXPORT_SYMBOL(__cpu_present_mask);