Skip to content

Commit

Permalink
ACPI: container: Add power domain control methods
Browse files Browse the repository at this point in the history
Platform devices which supports power control are often required to be
power off/on together with the devices in the same power domain. However,
there isn't a generic driver that support the power control logic of
these devices.

ACPI container seems to be a good place to hold these control logic. Add
platform devices in the same power domain in a ACPI container, we can
easily get the locality information about these devices and can moniter
the power of these devices in the same power domain together.

This patch provide three userspace control interface to control the power
of devices together in the container:
- on: power up the devices in the container and then online these devices
  which will be triggered by BIOS.
- off: offline and eject the child devices in the container which are
  ejectable.
- pxms: show the pxms of devices which are present in the container.

In our scenario, we need to control the power of HBM memory devices which
can be power consuming and will only be used in some specialized scenarios,
such as HPC. HBM memory devices in a socket are in the same power domain,
and should be power off/on together. We have come up with an idea that put
these power control logic in a specialized driver, but ACPI container seems
to be a more generic place to hold these control logic.

Signed-off-by: Zhang Zekun <zhangzekun11@huawei.com>
  • Loading branch information
Zhang Zekun authored and intel-lab-lkp committed Oct 25, 2022
1 parent 89871b5 commit d8c2ee6
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 0 deletions.
12 changes: 12 additions & 0 deletions drivers/acpi/Kconfig
Expand Up @@ -584,6 +584,18 @@ config ACPI_PRMT
substantially increase computational overhead related to the
initialization of some server systems.

config ACPI_POWER_DOMAIN_CTL
bool "acpi container power domain control support"
depends on ACPI_CONTAINER
default n
help
Add userspace power control interfaces in container which can be used
for manipulating the power of child devices in the same power domain.

To use this feature you need to put devices in the same power domain
in a container. Enable this feature if you want to control the power
of these devices together.

endif # ACPI

config X86_PM_TIMER
Expand Down
112 changes: 112 additions & 0 deletions drivers/acpi/container.c
Expand Up @@ -42,6 +42,115 @@ static void acpi_container_release(struct device *dev)
kfree(to_container_dev(dev));
}

#ifdef CONFIG_ACPI_POWER_DOMAIN_CTL

static int get_pxm(struct acpi_device *acpi_device, void *arg)
{
int nid;
unsigned long long sta;
acpi_handle handle;
nodemask_t *mask;
acpi_status status;

mask = arg;
handle = acpi_device->handle;

status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_ENABLED)) {
nid = acpi_get_node(handle);
if (nid >= 0)
node_set(nid, *mask);
}

return 0;
}

static ssize_t pxms_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
nodemask_t mask;
acpi_status status;
struct acpi_device *adev;

adev = to_acpi_device(dev);
nodes_clear(mask);

status = acpi_dev_for_each_child(adev, get_pxm, &mask);

return sysfs_emit(buf, "%*pbl\n", nodemask_pr_args(&mask));
}
DEVICE_ATTR_RO(pxms);

static ssize_t on_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t count)
{
acpi_status status;
acpi_handle handle;
struct acpi_device *adev;

if (!count || buf[0] != '1')
return -EINVAL;

adev = to_acpi_device(d);
handle = adev->handle;
status = acpi_evaluate_object(handle, "_ON", NULL, NULL);
if (status == AE_NOT_FOUND)
acpi_handle_warn(handle, "No power on support for the container\n");
else if (ACPI_FAILURE(status))
acpi_handle_warn(handle, "Power on the device failed (0x%x)\n", status);

return count;
}
DEVICE_ATTR_WO(on);

static int eject_device(struct acpi_device *acpi_device, void *not_used)
{
acpi_object_type unused;
acpi_status status;

status = acpi_get_type(acpi_device->handle, &unused);
if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)
return -ENODEV;

acpi_dev_get(acpi_device);
status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT);
if (ACPI_SUCCESS(status))
return status;

acpi_dev_put(acpi_device);
acpi_evaluate_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);

return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN;
}

static ssize_t off_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t count)
{
struct acpi_device *adev;
acpi_status status;

if (!count || buf[0] != '1')
return -EINVAL;

adev = to_acpi_device(d);
status = acpi_dev_for_each_child(adev, eject_device, NULL);
if (ACPI_SUCCESS(status))
return count;

return status;
}
DEVICE_ATTR_WO(off);

static void create_sysfs(struct device *dev)
{
device_create_file(dev, &dev_attr_on);
device_create_file(dev, &dev_attr_off);
device_create_file(dev, &dev_attr_pxms);
}
#endif

static int container_device_attach(struct acpi_device *adev,
const struct acpi_device_id *not_used)
{
Expand All @@ -68,6 +177,9 @@ static int container_device_attach(struct acpi_device *adev,
return ret;
}
adev->driver_data = dev;
#ifdef CONFIG_ACPI_POWER_DOMAIN_CTL
create_sysfs(&adev->dev);
#endif
return 1;
}

Expand Down

0 comments on commit d8c2ee6

Please sign in to comment.