Merge branches 'acpi-scan', 'acpi-glue', 'acpi-bus' and 'acpi-sysfs'

Merge ACPI support infrastructure changes for 7.4:

 - Make the core ACPI device enumeration code stop attempting to
   initialize ACPI power management for devices before they are
   known to be present and functional (Rafael Wysocki)

 - Simplify and clean up the core ACPI device enumeration code on top of
   the previous adjustment and reduce debug noise coming from it (Rafael
   Wysocki)

 - Fix locking in the code binding devices to their ACPI companions,
   prevent it from replacing a valid ACPI companion pointer with NULL
   incidentally, improve its messaging (including debug noise
   reduction), and reduce its overhead by making it skip the cases in
   which no ACPI companion may exist early (Rafael Wysocki)

 - Reduce runtime memory footprint of struct acpi_device by rearranging
   fields in it (Andy Shevchenko)

 - Update the driver core to allow bus types without drivers to be
   registered as "companion bus types" without driver-related sysfs
   attributes and register the ACPI bus type as a companion one (Rafael
   Wysocki)

 - Use strscpy() instead of strcpy() in the ACPI sysfs interface (Ruslan
   Vagner)

* acpi-scan:
  ACPI: scan: Combine two conditionals in acpi_bus_attach()
  ACPI: PM: Move acpi_bus_init_power() declaration to internal header file
  ACPI: scan: Stop calling acpi_bus_init_power() early
  ACPI: PM: Drop parent state update from acpi_device_get_power()
  ACPI: scan: Drop useless and noisy debug statement

* acpi-glue:
  ACPI: glue: Skip devices with no type in acpi_device_notify()
  ACPI: glue: Fix up and adjust acpi_unbind_one()
  ACPI: glue: Rearrange acpi_bind_one() to avoid breakage
  ACPI: glue: Carry out companion lookup under bus_type_sem
  ACPI: glue: Rework the success message in acpi_device_notify()
  ACPI: glue: Reduce debug noise from acpi_device_notify()

* acpi-bus:
  driver core/ACPI: Introduce companion_bus_register()
  ACPI: bus: Reduce runtime memory footprint of struct acpi_device

* acpi-sysfs:
  ACPI: sysfs: use strscpy() instead of strcpy()
This commit is contained in:
Rafael J. Wysocki 2026-10-16 21:17:17 +02:00
10 changed files with 214 additions and 151 deletions

View File

@ -1120,11 +1120,6 @@ EXPORT_SYMBOL_GPL(acpi_driver_match_device);
ACPI Bus operations
-------------------------------------------------------------------------- */
static int acpi_bus_match(struct device *dev, const struct device_driver *drv)
{
return 0;
}
static int acpi_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
return __acpi_device_uevent_modalias(to_acpi_device(dev), env);
@ -1132,7 +1127,6 @@ static int acpi_device_uevent(const struct device *dev, struct kobj_uevent_env *
const struct bus_type acpi_bus_type = {
.name = "acpi",
.match = acpi_bus_match,
.uevent = acpi_device_uevent,
};
@ -1451,7 +1445,7 @@ static int __init acpi_bus_init(void)
*/
acpi_root_dir = proc_mkdir(ACPI_BUS_FILE_ROOT, NULL);
result = bus_register(&acpi_bus_type);
result = companion_bus_register(&acpi_bus_type);
if (!result)
return 0;

View File

@ -23,6 +23,8 @@
#include "fan.h"
#include "internal.h"
#define ACPI_D_STATE_INVALID ACPI_D_STATE_COUNT
/**
* acpi_power_state_string - String representation of ACPI device power state.
* @state: ACPI device power state to return the string representation of.
@ -75,15 +77,14 @@ static int acpi_dev_pm_explicit_get(struct acpi_device *device, int *state)
int acpi_device_get_power(struct acpi_device *device, int *state)
{
int result = ACPI_STATE_UNKNOWN;
struct acpi_device *parent;
int error;
if (!device || !state)
return -EINVAL;
parent = acpi_dev_parent(device);
if (!device->flags.power_manageable) {
struct acpi_device *parent = acpi_dev_parent(device);
/* TBD: Non-recursive algorithm for walking up hierarchy. */
*state = parent ? parent->power.state : ACPI_STATE_D0;
goto out;
@ -119,16 +120,6 @@ int acpi_device_get_power(struct acpi_device *device, int *state)
result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_HOT : psc;
}
/*
* If we were unsure about the device parent's power state up to this
* point, the fact that the device is in D0 implies that the parent has
* to be in D0 too, except if ignore_parent is set.
*/
if (!device->power.flags.ignore_parent && parent &&
parent->power.state == ACPI_STATE_UNKNOWN &&
result == ACPI_STATE_D0)
parent->power.state = ACPI_STATE_D0;
*state = result;
out:
@ -304,24 +295,28 @@ int acpi_bus_set_power(acpi_handle handle, int state)
}
EXPORT_SYMBOL(acpi_bus_set_power);
int acpi_bus_init_power(struct acpi_device *device)
static int acpi_device_init_power(struct acpi_device *device)
{
int state;
int result;
if (!device)
return -EINVAL;
device->power.state = ACPI_STATE_UNKNOWN;
if (!acpi_device_is_present(device)) {
device->flags.initialized = false;
return -ENXIO;
}
result = acpi_device_get_power(device, &state);
if (result)
return result;
/*
* If the current power state of the device is D0 and it has a parent
* whose power state is not ignored, and the parent's power state
* initialization has failed, the parent's power state can be updated to
* D0 for consistency.
*/
if (!device->power.flags.ignore_parent && state == ACPI_STATE_D0) {
struct acpi_device *parent = acpi_dev_parent(device);
if (parent && parent->power.state == ACPI_D_STATE_INVALID)
parent->power.state = ACPI_STATE_D0;
}
if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) {
/* Reference count the power resources. */
result = acpi_power_on_resources(device, state);
@ -351,9 +346,36 @@ int acpi_bus_init_power(struct acpi_device *device)
state = ACPI_STATE_D0;
}
device->power.state = state;
acpi_handle_debug(device->handle, "Initial power state: %s\n",
acpi_power_state_string(state));
return 0;
}
int acpi_bus_init_power(struct acpi_device *device)
{
int result;
if (device->power.state != ACPI_STATE_UNKNOWN)
return 0;
/*
* The ACPI device power state can be only initialized once. If this
* fails, ACPI power management will not be used for the device going
* forward.
*/
result = acpi_device_init_power(device);
if (result) {
device->flags.power_manageable = 0;
device->power.state = ACPI_D_STATE_INVALID;
acpi_handle_info(device->handle,
"Initial power state undetermined, ACPI PM disabled\n");
}
return result;
}
/**
* acpi_device_fix_up_power - Force device with missing _PSC into D0.
* @device: Device object whose power state is to be fixed up.
@ -475,8 +497,13 @@ static int acpi_power_up_if_adr_present(struct acpi_device *adev, void *not_used
if (!(adev->flags.power_manageable && adev->pnp.type.bus_address))
return 0;
acpi_handle_debug(adev->handle, "Power state: %s\n",
acpi_power_state_string(adev->power.state));
/*
* This is done during the PCI root initialization which occurs before
* acpi_bus_attach() is called for the device, so the ACPI power state
* of the device needs to be initialized here.
*/
if (acpi_bus_init_power(adev))
return 0;
if (adev->power.state == ACPI_STATE_D3_COLD)
return acpi_device_set_power(adev, ACPI_STATE_D0);

View File

@ -59,19 +59,37 @@ int unregister_acpi_bus_type(struct acpi_bus_type *type)
}
EXPORT_SYMBOL_GPL(unregister_acpi_bus_type);
static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
static struct acpi_device *acpi_companion_lookup(struct device *dev)
{
struct acpi_bus_type *tmp, *ret = NULL;
struct acpi_bus_type *type;
down_read(&bus_type_sem);
list_for_each_entry(tmp, &bus_type_list, list) {
if (tmp->match(dev)) {
ret = tmp;
break;
if (!dev->type)
return NULL;
guard(rwsem_read)(&bus_type_sem);
list_for_each_entry(type, &bus_type_list, list) {
struct acpi_device *adev;
if (!type->match(dev))
continue;
adev = type->find_companion(dev);
if (!adev) {
dev_dbg(dev, "ACPI companion not found\n");
return NULL;
}
if (acpi_bind_one(dev, adev)) {
dev_dbg(dev, "Binding to ACPI companion failed\n");
return NULL;
}
if (type->setup)
type->setup(dev);
return adev;
}
up_read(&bus_type_sem);
return ret;
return NULL;
}
#define FIND_CHILD_MIN_SCORE 1
@ -228,31 +246,25 @@ static void acpi_physnode_link_name(char *buf, unsigned int node_id)
int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
{
struct acpi_device_physical_node *physical_node, *pn;
struct acpi_device *comp_dev = ACPI_COMPANION(dev);
char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
struct list_head *physnode_list;
unsigned int node_id;
int retval = -EINVAL;
if (has_acpi_companion(dev)) {
if (acpi_dev) {
dev_warn(dev, "ACPI companion already set\n");
if (!acpi_dev) {
if (!comp_dev)
return -EINVAL;
} else {
acpi_dev = ACPI_COMPANION(dev);
}
}
if (!acpi_dev)
return -EINVAL;
acpi_dev_get(acpi_dev);
get_device(dev);
physical_node = kzalloc_obj(*physical_node);
if (!physical_node) {
retval = -ENOMEM;
goto err;
/* If the companion has been set upfront, pick it up. */
acpi_dev = comp_dev;
} else if (comp_dev && acpi_dev != comp_dev) {
dev_warn(dev, "ACPI companion already set to %s which is not %s\n",
acpi_dev_name(comp_dev), acpi_dev_name(acpi_dev));
return -EEXIST;
}
mutex_lock(&acpi_dev->physical_node_lock);
guard(mutex)(&acpi_dev->physical_node_lock);
/*
* Keep the list sorted by node_id so that the IDs of removed nodes can
@ -263,15 +275,12 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
list_for_each_entry(pn, &acpi_dev->physical_node_list, node) {
/* Sanity check. */
if (pn->dev == dev) {
mutex_unlock(&acpi_dev->physical_node_lock);
dev_warn(dev, "Already associated with ACPI node\n");
kfree(physical_node);
if (ACPI_COMPANION(dev) != acpi_dev)
goto err;
put_device(dev);
acpi_dev_put(acpi_dev);
if (!comp_dev) {
/* Really unexpected. */
ACPI_COMPANION_SET(dev, acpi_dev);
dev_warn(&acpi_dev->dev,
"Physical device list corruption fixed up\n");
}
return 0;
}
if (pn->node_id == node_id) {
@ -280,12 +289,19 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
}
}
physical_node = kzalloc_obj(*physical_node);
if (!physical_node)
return -ENOMEM;
acpi_dev_get(acpi_dev);
get_device(dev);
physical_node->node_id = node_id;
physical_node->dev = dev;
list_add(&physical_node->node, physnode_list);
acpi_dev->physical_node_count++;
if (!has_acpi_companion(dev))
if (!comp_dev)
ACPI_COMPANION_SET(dev, acpi_dev);
acpi_physnode_link_name(physical_node_name, node_id);
@ -301,28 +317,20 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
dev_err(dev, "Failed to create link firmware_node (%d)\n",
retval);
mutex_unlock(&acpi_dev->physical_node_lock);
if (acpi_dev->wakeup.flags.valid)
device_set_wakeup_capable(dev, true);
return 0;
err:
ACPI_COMPANION_SET(dev, NULL);
put_device(dev);
acpi_dev_put(acpi_dev);
return retval;
}
EXPORT_SYMBOL_GPL(acpi_bind_one);
int acpi_unbind_one(struct device *dev)
void acpi_unbind_one(struct device *dev)
{
struct acpi_device *acpi_dev = ACPI_COMPANION(dev);
struct acpi_device_physical_node *entry;
if (!acpi_dev)
return 0;
return;
mutex_lock(&acpi_dev->physical_node_lock);
@ -337,15 +345,17 @@ int acpi_unbind_one(struct device *dev)
sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name);
sysfs_remove_link(&dev->kobj, "firmware_node");
ACPI_COMPANION_SET(dev, NULL);
mutex_unlock(&acpi_dev->physical_node_lock);
/* Drop references taken by acpi_bind_one(). */
put_device(dev);
acpi_dev_put(acpi_dev);
kfree(entry);
break;
return;
}
mutex_unlock(&acpi_dev->physical_node_lock);
return 0;
}
EXPORT_SYMBOL_GPL(acpi_unbind_one);
@ -354,48 +364,29 @@ void acpi_device_notify(struct device *dev)
struct acpi_device *adev;
int ret;
/* ACPI devices have no ACPI companions. */
if (dev->bus == &acpi_bus_type)
return;
ret = acpi_bind_one(dev, NULL);
if (ret) {
struct acpi_bus_type *type = acpi_get_bus_type(dev);
if (!type)
goto err;
adev = type->find_companion(dev);
if (!adev) {
dev_dbg(dev, "ACPI companion not found\n");
goto err;
}
ret = acpi_bind_one(dev, adev);
if (ret)
goto err;
if (type->setup) {
type->setup(dev);
goto done;
}
adev = acpi_companion_lookup(dev);
if (!adev)
return;
} else {
adev = ACPI_COMPANION(dev);
if (dev_is_pci(dev)) {
pci_acpi_setup(dev, adev);
goto done;
} else if (dev_is_platform(dev)) {
acpi_configure_pmsi_domain(dev);
if (adev->handler && adev->handler->bind)
adev->handler->bind(dev);
}
}
if (adev->handler && adev->handler->bind)
adev->handler->bind(dev);
done:
acpi_handle_debug(ACPI_HANDLE(dev), "Bound to device %s\n",
dev_name(dev));
return;
err:
dev_dbg(dev, "No ACPI support\n");
dev_dbg(dev, "Bound to ACPI device %s\n", acpi_dev_name(adev));
}
void acpi_device_notify_remove(struct device *dev)

View File

@ -157,6 +157,7 @@ void acpi_turn_off_unused_power_resources(void);
Device Power Management
-------------------------------------------------------------------------- */
int acpi_device_get_power(struct acpi_device *device, int *state);
int acpi_bus_init_power(struct acpi_device *device);
int acpi_wakeup_device_init(void);
/* --------------------------------------------------------------------------

View File

@ -20,6 +20,7 @@
#include <linux/kthread.h>
#include <linux/dmi.h>
#include <linux/dma-map-ops.h>
#include <linux/pci.h>
#include <linux/platform_data/x86/apple.h>
#include <linux/pgtable.h>
#include <linux/crc32.h>
@ -637,10 +638,9 @@ static struct acpi_device *handle_to_device(acpi_handle handle,
status = acpi_get_data_full(handle, acpi_scan_drop_device,
(void **)&adev, callback);
if (ACPI_FAILURE(status) || !adev) {
acpi_handle_debug(handle, "No context!\n");
if (ACPI_FAILURE(status) || !adev)
return NULL;
}
return adev;
}
@ -1143,8 +1143,7 @@ static void acpi_bus_get_power_flags(struct acpi_device *device)
device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1;
}
if (acpi_bus_init_power(device))
device->flags.power_manageable = 0;
device->power.state = ACPI_STATE_UNKNOWN;
}
static void acpi_bus_get_flags(struct acpi_device *device)
@ -2338,50 +2337,57 @@ static int acpi_scan_attach_handler(struct acpi_device *device)
static int acpi_bus_attach(struct acpi_device *device, void *first_pass)
{
bool skip = !first_pass && device->flags.visited;
struct pci_dev *pci;
acpi_handle ejd;
int ret;
if (skip)
goto ok;
device->flags.initialized = true;
if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd)))
register_dock_dependent_device(device, ejd);
acpi_bus_get_status(device);
/* Skip devices that are not ready for enumeration (e.g. not present) */
if (!acpi_dev_ready_for_enumeration(device)) {
device->flags.initialized = false;
/*
* If the given ACPI device object has been already associated with a
* PCI device found on the bus, its status is effectively "present and
* enabled", and dependencies are not tracked for PCI devices, so it is
* not necessary or even useful to check the device's readiness in that
* case.
*/
pci = acpi_dev_get_pci_dev(device);
if (pci) {
acpi_handle_debug(device->handle, "PCI companion %s found\n",
pci_name(pci));
if (!acpi_device_is_present(device))
pci_info(pci, FW_BUG "ACPI status differs from reality\n");
pci_dev_put(pci);
} else if (!acpi_dev_ready_for_enumeration(device)) {
/* The device is not ready (e.g. not present), so skip it. */
acpi_device_clear_enumerated(device);
device->flags.power_manageable = 0;
return 0;
}
if (device->handler)
goto ok;
acpi_ec_register_opregions(device);
if (!device->flags.initialized) {
device->flags.power_manageable =
device->power.states[ACPI_STATE_D0].flags.valid;
if (acpi_bus_init_power(device))
device->flags.power_manageable = 0;
acpi_bus_init_power(device);
device->flags.initialized = true;
} else if (device->flags.visited) {
if (device->flags.visited)
goto ok;
}
ret = acpi_scan_attach_handler(device);
if (ret < 0)
return 0;
if (ret > 0 && !device->flags.enumeration_by_parent) {
acpi_device_set_enumerated(device);
goto ok;
}
if (device->pnp.type.platform_id || device->pnp.type.backlight ||
device->flags.enumeration_by_parent)
if (device->flags.enumeration_by_parent ||
(!ret && (device->pnp.type.platform_id || device->pnp.type.backlight)))
acpi_default_enumeration(device);
else
acpi_device_set_enumerated(device);

View File

@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/kstrtox.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
#include "internal.h"
@ -181,10 +182,10 @@ static int param_set_trace_method_name(const char *val,
/* This is a hack. We can't kmalloc in early boot. */
if (is_abs_path)
strcpy(trace_method_name, val);
strscpy(trace_method_name, val, sizeof(trace_method_name));
else {
trace_method_name[0] = '\\';
strcpy(trace_method_name+1, val);
strscpy(trace_method_name + 1, val, sizeof(trace_method_name) - 1);
}
/* Restore the original tracer state */

View File

@ -27,6 +27,7 @@
* @drivers_autoprobe: gate whether new devices are automatically attached to
* registered drivers, or new drivers automatically attach
* to existing devices.
* @no_drivers: gate whether drivers can be registered.
* @bus: pointer back to the struct bus_type that this structure is associated
* with.
* @dev_root: Default device to use as the parent.
@ -51,6 +52,7 @@ struct subsys_private {
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
unsigned int no_drivers:1;
const struct bus_type *bus;
struct device *dev_root;

View File

@ -738,6 +738,11 @@ int bus_add_driver(struct device_driver *drv)
if (!sp)
return -EINVAL;
if (sp->no_drivers) {
error = -ENXIO;
goto out_put_bus;
}
/*
* Reference in sp is now incremented and will be dropped when
* the driver is removed from the bus
@ -930,15 +935,7 @@ static ssize_t bus_uevent_store(const struct bus_type *bus,
static struct bus_attribute bus_attr_uevent = __ATTR(uevent, 0200, NULL,
bus_uevent_store);
/**
* bus_register - register a driver-core subsystem
* @bus: bus to register
*
* Once we have that, we register the bus with the kobject
* infrastructure, then register the children subsystems it has:
* the devices and drivers that belong to the subsystem.
*/
int bus_register(const struct bus_type *bus)
static int bus_register_internal(const struct bus_type *bus, bool use_drivers)
{
int retval;
struct subsys_private *priv;
@ -960,7 +957,8 @@ int bus_register(const struct bus_type *bus)
bus_kobj->kset = bus_kset;
bus_kobj->ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
priv->drivers_autoprobe = use_drivers;
priv->no_drivers = !use_drivers;
retval = kset_register(&priv->subsys);
if (retval)
@ -976,6 +974,10 @@ int bus_register(const struct bus_type *bus)
goto bus_devices_fail;
}
/*
* Create the drivers kset even if use_drivers is unset to prevent some
* older versions of systemd from failing.
*/
priv->drivers_kset = kset_create_and_add("drivers", NULL, bus_kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
@ -989,9 +991,11 @@ int bus_register(const struct bus_type *bus)
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
if (use_drivers) {
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
}
retval = sysfs_create_groups(bus_kobj, bus->bus_groups);
if (retval)
@ -1016,8 +1020,40 @@ int bus_register(const struct bus_type *bus)
kfree(priv);
return retval;
}
/**
* bus_register - register a driver-core subsystem
* @bus: bus to register
*
* Once we have that, we register the bus with the kobject
* infrastructure, then register the children subsystems it has:
* the devices and drivers that belong to the subsystem.
*/
int bus_register(const struct bus_type *bus)
{
return bus_register_internal(bus, true);
}
EXPORT_SYMBOL_GPL(bus_register);
/**
* companion_bus_register - register a companion bus type
* @bus: companion bus to register
*
* A companion bus is a bus without drivers. Devices that belong to it can be
* bound to other devices as their "companions" and represent interfaces that
* can be used by the drivers of those other devices. They may also be used for
* the enumeration of those other devices.
*
* The ACPI bus is a specific example of a companion bus.
*
* Registering a companion bus is like registering a regular bus except that it
* skips the creation of sysfs interfaces related to drivers for @bus.
*/
int companion_bus_register(const struct bus_type *bus)
{
return bus_register_internal(bus, false);
}
/**
* bus_unregister - remove a bus from the system
* @bus: bus.
@ -1415,6 +1451,11 @@ struct device_driver *driver_find(const char *name, const struct bus_type *bus)
if (!sp)
return NULL;
if (sp->no_drivers) {
subsys_put(sp);
return NULL;
}
k = kset_find_obj(sp->drivers_kset, name);
subsys_put(sp);
if (!k)

View File

@ -435,9 +435,9 @@ struct acpi_device_software_nodes {
/* Device */
struct acpi_device {
acpi_handle handle; /* no handle for fixed hardware */
u32 pld_crc;
int device_type;
acpi_handle handle; /* no handle for fixed hardware */
struct fwnode_handle fwnode;
struct list_head wakeup_list;
struct list_head del_list;
@ -613,7 +613,6 @@ int acpi_bus_get_status(struct acpi_device *device);
int acpi_bus_set_power(acpi_handle handle, int state);
const char *acpi_power_state_string(int state);
int acpi_device_set_power(struct acpi_device *device, int state);
int acpi_bus_init_power(struct acpi_device *device);
int acpi_device_fix_up_power(struct acpi_device *device);
void acpi_device_fix_up_power_extended(struct acpi_device *adev);
void acpi_device_fix_up_power_children(struct acpi_device *adev);
@ -665,7 +664,7 @@ struct acpi_bus_type {
int register_acpi_bus_type(struct acpi_bus_type *);
int unregister_acpi_bus_type(struct acpi_bus_type *);
int acpi_bind_one(struct device *dev, struct acpi_device *adev);
int acpi_unbind_one(struct device *dev);
void acpi_unbind_one(struct device *dev);
enum acpi_bridge_type {
ACPI_BRIDGE_TYPE_PCIE = 1,

View File

@ -113,6 +113,7 @@ struct bus_type {
bool need_parent_lock;
};
int __must_check companion_bus_register(const struct bus_type *bus);
int __must_check bus_register(const struct bus_type *bus);
void bus_unregister(const struct bus_type *bus);