Merge branch 'acpi-processor'

Merge ACPI processor driver changes for 6.20-rc1/7.0-rc1:

 - Rework the ACPI idle driver initialization to register it directly
   from the common initialization code instead of doing that from a
   CPU hotplug "online" callback and clean it up (Huisong Li, Rafael
   Wysocki)

 - Fix a possible NULL pointer dereference in
   acpi_processor_errata_piix4() (Tuo Li)

* acpi-processor:
  ACPI: processor: idle: Rework the handling of acpi_processor_ffh_lpi_probe()
  ACPI: processor: idle: Convert acpi_processor_setup_cpuidle_dev() to void
  ACPI: processor: idle: Convert acpi_processor_setup_cpuidle_states() to void
  ACPI: processor: idle: Add debug log for states with invalid entry methods
  ACPI: processor: Fix NULL-pointer dereference in acpi_processor_errata_piix4()
  ACPI: processor: Do not expose global variable acpi_idle_driver
  ACPI: processor: idle: Rearrange declarations in header file
  ACPI: processor: idle: Redefine two functions as void
  ACPI: processor: Update cpuidle driver check in __acpi_processor_start()
  ACPI: processor: Remove unused empty stubs of some functions
  ACPI: processor: idle: Optimize ACPI idle driver registration
This commit is contained in:
Rafael J. Wysocki 2026-02-05 15:09:30 +01:00
commit 2b0181a52f
4 changed files with 126 additions and 111 deletions

View file

@ -50,6 +50,7 @@ static int acpi_processor_errata_piix4(struct pci_dev *dev)
{
u8 value1 = 0;
u8 value2 = 0;
struct pci_dev *ide_dev = NULL, *isa_dev = NULL;
if (!dev)
@ -107,12 +108,12 @@ static int acpi_processor_errata_piix4(struct pci_dev *dev)
* each IDE controller's DMA status to make sure we catch all
* DMA activity.
*/
dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
ide_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82371AB,
PCI_ANY_ID, PCI_ANY_ID, NULL);
if (dev) {
errata.piix4.bmisx = pci_resource_start(dev, 4);
pci_dev_put(dev);
if (ide_dev) {
errata.piix4.bmisx = pci_resource_start(ide_dev, 4);
pci_dev_put(ide_dev);
}
/*
@ -124,24 +125,25 @@ static int acpi_processor_errata_piix4(struct pci_dev *dev)
* disable C3 support if this is enabled, as some legacy
* devices won't operate well if fast DMA is disabled.
*/
dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
isa_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82371AB_0,
PCI_ANY_ID, PCI_ANY_ID, NULL);
if (dev) {
pci_read_config_byte(dev, 0x76, &value1);
pci_read_config_byte(dev, 0x77, &value2);
if (isa_dev) {
pci_read_config_byte(isa_dev, 0x76, &value1);
pci_read_config_byte(isa_dev, 0x77, &value2);
if ((value1 & 0x80) || (value2 & 0x80))
errata.piix4.fdma = 1;
pci_dev_put(dev);
pci_dev_put(isa_dev);
}
break;
}
if (errata.piix4.bmisx)
dev_dbg(&dev->dev, "Bus master activity detection (BM-IDE) erratum enabled\n");
if (errata.piix4.fdma)
dev_dbg(&dev->dev, "Type-F DMA livelock erratum (C3 disabled)\n");
if (ide_dev)
dev_dbg(&ide_dev->dev, "Bus master activity detection (BM-IDE) erratum enabled\n");
if (isa_dev)
dev_dbg(&isa_dev->dev, "Type-F DMA livelock erratum (C3 disabled)\n");
return 0;
}

View file

@ -166,8 +166,7 @@ static int __acpi_processor_start(struct acpi_device *device)
if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
dev_dbg(&device->dev, "CPPC data invalid or not present\n");
if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
acpi_processor_power_init(pr);
acpi_processor_power_init(pr);
acpi_pss_perf_init(pr);
@ -259,9 +258,11 @@ static int __init acpi_processor_driver_init(void)
acpi_processor_ignore_ppc_init();
}
acpi_processor_register_idle_driver();
result = driver_register(&acpi_processor_driver);
if (result < 0)
return result;
goto unregister_idle_drv;
result = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"acpi/cpu-drv:online",
@ -283,8 +284,13 @@ static int __init acpi_processor_driver_init(void)
acpi_idle_rescan_dead_smt_siblings();
return 0;
err:
driver_unregister(&acpi_processor_driver);
unregister_idle_drv:
acpi_processor_unregister_idle_driver();
return result;
}
@ -302,6 +308,7 @@ static void __exit acpi_processor_driver_exit(void)
cpuhp_remove_state_nocalls(hp_online);
cpuhp_remove_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD);
driver_unregister(&acpi_processor_driver);
acpi_processor_unregister_idle_driver();
}
module_init(acpi_processor_driver_init);

View file

@ -51,7 +51,7 @@ module_param(latency_factor, uint, 0644);
static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device);
struct cpuidle_driver acpi_idle_driver = {
static struct cpuidle_driver acpi_idle_driver = {
.name = "acpi_idle",
.owner = THIS_MODULE,
};
@ -946,6 +946,8 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle,
lpi_state->entry_method = ACPI_CSTATE_INTEGER;
lpi_state->address = obj->integer.value;
} else {
pr_debug("Entry method of state-%d is invalid, disable it.\n",
state_idx);
continue;
}
@ -1178,7 +1180,7 @@ static int acpi_idle_lpi_enter(struct cpuidle_device *dev,
return -EINVAL;
}
static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
static void acpi_processor_setup_lpi_states(struct acpi_processor *pr)
{
int i;
struct acpi_lpi_state *lpi;
@ -1186,7 +1188,7 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
struct cpuidle_driver *drv = &acpi_idle_driver;
if (!pr->flags.has_lpi)
return -EOPNOTSUPP;
return;
for (i = 0; i < pr->power.count && i < CPUIDLE_STATE_MAX; i++) {
lpi = &pr->power.lpi_states[i];
@ -1204,8 +1206,6 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
}
drv->state_count = i;
return 0;
}
/**
@ -1214,13 +1214,13 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
*
* @pr: the ACPI processor
*/
static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
static void acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
{
int i;
struct cpuidle_driver *drv = &acpi_idle_driver;
if (!pr->flags.power_setup_done || !pr->flags.power)
return -EINVAL;
return;
drv->safe_state_index = -1;
for (i = ACPI_IDLE_STATE_START; i < CPUIDLE_STATE_MAX; i++) {
@ -1228,32 +1228,30 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
drv->states[i].desc[0] = '\0';
}
if (pr->flags.has_lpi)
return acpi_processor_setup_lpi_states(pr);
if (pr->flags.has_lpi) {
acpi_processor_setup_lpi_states(pr);
return;
}
acpi_processor_setup_cstates(pr);
return 0;
}
/**
* acpi_processor_setup_cpuidle_dev - prepares and configures CPUIDLE
* acpi_processor_setup_cpuidle_dev - configures CPUIDLE
* device i.e. per-cpu data
*
* @pr: the ACPI processor
* @dev : the cpuidle device
*/
static int acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr,
struct cpuidle_device *dev)
static void acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr,
struct cpuidle_device *dev)
{
if (!pr->flags.power_setup_done || !pr->flags.power || !dev)
return -EINVAL;
return;
dev->cpu = pr->id;
if (pr->flags.has_lpi)
return acpi_processor_ffh_lpi_probe(pr->id);
acpi_processor_setup_cpuidle_cx(pr, dev);
return 0;
if (!pr->flags.has_lpi)
acpi_processor_setup_cpuidle_cx(pr, dev);
}
static int acpi_processor_get_power_info(struct acpi_processor *pr)
@ -1262,7 +1260,13 @@ static int acpi_processor_get_power_info(struct acpi_processor *pr)
ret = acpi_processor_get_lpi_info(pr);
if (ret)
ret = acpi_processor_get_cstate_info(pr);
return acpi_processor_get_cstate_info(pr);
if (pr->flags.has_lpi) {
ret = acpi_processor_ffh_lpi_probe(pr->id);
if (ret)
pr_err("CPU%u: Invalid FFH LPI data\n", pr->id);
}
return ret;
}
@ -1347,79 +1351,103 @@ int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
return 0;
}
static int acpi_processor_registered;
int acpi_processor_power_init(struct acpi_processor *pr)
void acpi_processor_register_idle_driver(void)
{
struct acpi_processor *pr;
int ret = -ENODEV;
int cpu;
/*
* ACPI idle driver is used by all possible CPUs.
* Use the processor power info of one in them to set up idle states.
* Note that the existing idle handler will be used on platforms that
* only support C1.
*/
for_each_possible_cpu(cpu) {
pr = per_cpu(processors, cpu);
if (!pr)
continue;
acpi_processor_cstate_first_run_checks();
ret = acpi_processor_get_power_info(pr);
if (!ret) {
pr->flags.power_setup_done = 1;
acpi_processor_setup_cpuidle_states(pr);
break;
}
}
if (ret) {
pr_debug("No ACPI power information from any CPUs.\n");
return;
}
ret = cpuidle_register_driver(&acpi_idle_driver);
if (ret) {
pr_debug("register %s failed.\n", acpi_idle_driver.name);
return;
}
pr_debug("%s registered with cpuidle.\n", acpi_idle_driver.name);
}
void acpi_processor_unregister_idle_driver(void)
{
cpuidle_unregister_driver(&acpi_idle_driver);
}
void acpi_processor_power_init(struct acpi_processor *pr)
{
int retval;
struct cpuidle_device *dev;
/*
* The code below only works if the current cpuidle driver is the ACPI
* idle driver.
*/
if (cpuidle_get_driver() != &acpi_idle_driver)
return;
if (disabled_by_idle_boot_param())
return 0;
return;
acpi_processor_cstate_first_run_checks();
if (!acpi_processor_get_power_info(pr))
pr->flags.power_setup_done = 1;
if (!pr->flags.power)
return;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return;
per_cpu(acpi_cpuidle_device, pr->id) = dev;
acpi_processor_setup_cpuidle_dev(pr, dev);
/*
* Install the idle handler if processor power management is supported.
* Note that we use previously set idle handler will be used on
* platforms that only support C1.
* Register a cpuidle device for this CPU. The cpuidle driver using
* this device is expected to be registered.
*/
if (pr->flags.power) {
/* Register acpi_idle_driver if not already registered */
if (!acpi_processor_registered) {
acpi_processor_setup_cpuidle_states(pr);
retval = cpuidle_register_driver(&acpi_idle_driver);
if (retval)
return retval;
pr_debug("%s registered with cpuidle\n",
acpi_idle_driver.name);
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
per_cpu(acpi_cpuidle_device, pr->id) = dev;
acpi_processor_setup_cpuidle_dev(pr, dev);
/* Register per-cpu cpuidle_device. Cpuidle driver
* must already be registered before registering device
*/
retval = cpuidle_register_device(dev);
if (retval) {
if (acpi_processor_registered == 0)
cpuidle_unregister_driver(&acpi_idle_driver);
per_cpu(acpi_cpuidle_device, pr->id) = NULL;
kfree(dev);
return retval;
}
acpi_processor_registered++;
if (cpuidle_register_device(dev)) {
per_cpu(acpi_cpuidle_device, pr->id) = NULL;
kfree(dev);
}
return 0;
}
int acpi_processor_power_exit(struct acpi_processor *pr)
void acpi_processor_power_exit(struct acpi_processor *pr)
{
struct cpuidle_device *dev = per_cpu(acpi_cpuidle_device, pr->id);
if (disabled_by_idle_boot_param())
return 0;
return;
if (pr->flags.power) {
cpuidle_unregister_device(dev);
acpi_processor_registered--;
if (acpi_processor_registered == 0)
cpuidle_unregister_driver(&acpi_idle_driver);
kfree(dev);
}
pr->flags.power_setup_done = 0;
return 0;
}
MODULE_IMPORT_NS("ACPI_PROCESSOR_IDLE");

View file

@ -417,32 +417,15 @@ static inline void acpi_processor_throttling_init(void) {}
#endif /* CONFIG_ACPI_CPU_FREQ_PSS */
/* in processor_idle.c */
extern struct cpuidle_driver acpi_idle_driver;
#ifdef CONFIG_ACPI_PROCESSOR_IDLE
int acpi_processor_power_init(struct acpi_processor *pr);
int acpi_processor_power_exit(struct acpi_processor *pr);
void acpi_processor_power_init(struct acpi_processor *pr);
void acpi_processor_power_exit(struct acpi_processor *pr);
int acpi_processor_power_state_has_changed(struct acpi_processor *pr);
int acpi_processor_hotplug(struct acpi_processor *pr);
#else
static inline int acpi_processor_power_init(struct acpi_processor *pr)
{
return -ENODEV;
}
static inline int acpi_processor_power_exit(struct acpi_processor *pr)
{
return -ENODEV;
}
static inline int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
{
return -ENODEV;
}
static inline int acpi_processor_hotplug(struct acpi_processor *pr)
{
return -ENODEV;
}
void acpi_processor_register_idle_driver(void);
void acpi_processor_unregister_idle_driver(void);
int acpi_processor_ffh_lpi_probe(unsigned int cpu);
int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi);
#endif /* CONFIG_ACPI_PROCESSOR_IDLE */
/* in processor_thermal.c */
@ -465,11 +448,6 @@ static inline void acpi_thermal_cpufreq_exit(struct cpufreq_policy *policy)
}
#endif /* CONFIG_CPU_FREQ */
#ifdef CONFIG_ACPI_PROCESSOR_IDLE
extern int acpi_processor_ffh_lpi_probe(unsigned int cpu);
extern int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi);
#endif
void acpi_processor_init_invariance_cppc(void);
#endif