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

ACPI: processor: Fix evaluating _PDC method when running as Xen dom0

In ACPI systems, the OS can direct power management, as opposed to the
firmware. This OS-directed Power Management is called OSPM. Part of
telling the firmware that the OS going to direct power management is
making ACPI "_PDC" (Processor Driver Capabilities) calls. These _PDC
methods must be evaluated for every processor object. If these _PDC
calls are not completed for every processor it can lead to
inconsistency and later failures in things like the CPU frequency
driver.

In a Xen system, the dom0 kernel is responsible for system-wide power
management. The dom0 kernel is in charge of OSPM. However, the
number of CPUs available to dom0 can be different than the number of
CPUs physically present on the system.

This leads to a problem: the dom0 kernel needs to evaluate _PDC for
all the processors, but it can't always see them.

In dom0 kernels, ignore the existing ACPI method for determining if a
processor is physically present because it might not be accurate.
Instead, ask the hypervisor for this information.

Fix this by introducing a custom function to use when running as Xen
dom0 in order to check whether a processor object matches a CPU that's
online. Such checking is done using the existing information fetched
by the Xen pCPU subsystem, extending it to also store the ACPI ID.

This ensures that _PDC method gets evaluated for all physically online
CPUs, regardless of the number of CPUs made available to dom0.

Fixes: 5d554a7bb064 ("ACPI: processor: add internal processor_physically_present()")
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Juergen Gross <jgross@suse.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Roger Pau Monne and committed by
Rafael J. Wysocki
073828e9 691a6371

+42
+11
drivers/acpi/processor_pdc.c
··· 14 14 #include <linux/acpi.h> 15 15 #include <acpi/processor.h> 16 16 17 + #include <xen/xen.h> 18 + 17 19 #include "internal.h" 18 20 19 21 static bool __init processor_physically_present(acpi_handle handle) ··· 48 46 default: 49 47 return false; 50 48 } 49 + 50 + if (xen_initial_domain()) 51 + /* 52 + * When running as a Xen dom0 the number of processors Linux 53 + * sees can be different from the real number of processors on 54 + * the system, and we still need to execute _PDC for all of 55 + * them. 56 + */ 57 + return xen_processor_present(acpi_id); 51 58 52 59 type = (acpi_type == ACPI_TYPE_DEVICE) ? 1 : 0; 53 60 cpuid = acpi_get_cpuid(handle, type, acpi_id);
+20
drivers/xen/pcpu.c
··· 58 58 struct list_head list; 59 59 struct device dev; 60 60 uint32_t cpu_id; 61 + uint32_t acpi_id; 61 62 uint32_t flags; 62 63 }; 63 64 ··· 250 249 251 250 INIT_LIST_HEAD(&pcpu->list); 252 251 pcpu->cpu_id = info->xen_cpuid; 252 + pcpu->acpi_id = info->acpi_id; 253 253 pcpu->flags = info->flags; 254 254 255 255 /* Need hold on xen_pcpu_lock before pcpu list manipulations */ ··· 383 381 return ret; 384 382 } 385 383 arch_initcall(xen_pcpu_init); 384 + 385 + #ifdef CONFIG_ACPI 386 + bool __init xen_processor_present(uint32_t acpi_id) 387 + { 388 + const struct pcpu *pcpu; 389 + bool online = false; 390 + 391 + mutex_lock(&xen_pcpu_lock); 392 + list_for_each_entry(pcpu, &xen_pcpus, list) 393 + if (pcpu->acpi_id == acpi_id) { 394 + online = pcpu->flags & XEN_PCPU_FLAGS_ONLINE; 395 + break; 396 + } 397 + mutex_unlock(&xen_pcpu_lock); 398 + 399 + return online; 400 + } 401 + #endif
+11
include/xen/xen.h
··· 71 71 } 72 72 #endif 73 73 74 + #if defined(CONFIG_XEN_DOM0) && defined(CONFIG_ACPI) && defined(CONFIG_X86) 75 + bool __init xen_processor_present(uint32_t acpi_id); 76 + #else 77 + #include <linux/bug.h> 78 + static inline bool xen_processor_present(uint32_t acpi_id) 79 + { 80 + BUG(); 81 + return false; 82 + } 83 + #endif 84 + 74 85 #endif /* _XEN_XEN_H */