v4: - prettify the code
v3: - prettify the code - add a hisi_internal.h to hold common code
v2: - remove the !adev judge in patch 9, as it will always be true.
patch 1-3: Add support for iterate through the child devices in the acpi device.
patch 4-9: Add support for hbm memory device and hbm cache support
Rafael J. Wysocki (3): ACPI: bus: Introduce acpi_dev_for_each_child() ACPI: bus: Avoid non-ACPI device objects in walks over children ACPI: bus: Export acpi_dev_for_each_child() to modules
Zhang Zekun (6): ACPI: container: export the container list in the system ACPI: OSL: Export the symbol of acpi_hotplug_schedule soc: hisilicon: hisi_hbmdev: Add power domain control methods ACPI: memhotplug: export the state of each hotplug device soc: hisilicon: hisi_hbmdev: Provide extra memory topology information ACPI: hbmcache: Add support for online and offline the hbm cache
drivers/acpi/acpi_memhotplug.c | 6 + drivers/acpi/bus.c | 27 ++++ drivers/acpi/container.c | 51 ++++++ drivers/acpi/internal.h | 1 - drivers/acpi/osl.c | 1 + drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/hisilicon/Kconfig | 33 ++++ drivers/soc/hisilicon/Makefile | 4 + drivers/soc/hisilicon/hisi_hbmcache.c | 147 +++++++++++++++++ drivers/soc/hisilicon/hisi_hbmdev.c | 218 ++++++++++++++++++++++++++ drivers/soc/hisilicon/hisi_internal.h | 31 ++++ include/acpi/acpi_bus.h | 2 + include/linux/acpi.h | 1 + include/linux/container.h | 8 + include/linux/memory_hotplug.h | 2 + 16 files changed, 533 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/hisilicon/Kconfig create mode 100644 drivers/soc/hisilicon/Makefile create mode 100644 drivers/soc/hisilicon/hisi_hbmcache.c create mode 100644 drivers/soc/hisilicon/hisi_hbmdev.c create mode 100644 drivers/soc/hisilicon/hisi_internal.h
From: "Rafael J. Wysocki" rafael.j.wysocki@intel.com
mainline inclusion from mainline-v5.19-rc commit cf6ba0750a22a54f5101986401271429995cc4a0 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I67QNJ CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?...
--------------------------------------------
Introduce a wrapper around device_for_each_child() to iterate over the children of a given ACPI device object.
This function will be used in subsequent change sets.
Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Reviewed-by: Mika Westerberg mika.westerberg@linux.intel.com Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- drivers/acpi/bus.c | 6 ++++++ include/acpi/acpi_bus.h | 2 ++ 2 files changed, 8 insertions(+)
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 4ee50f4f6f01..d8a6b32a17ad 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -963,6 +963,12 @@ struct bus_type acpi_bus_type = { .uevent = acpi_device_uevent, };
+int acpi_dev_for_each_child(struct acpi_device *adev, + int (*fn)(struct device *, void *), void *data) +{ + return device_for_each_child(&adev->dev, data, fn); +} + /* -------------------------------------------------------------------------- Initialization/Cleanup -------------------------------------------------------------------------- */ diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index b5ff52052db3..d16a181d8a0e 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -494,6 +494,8 @@ void acpi_bus_detach_private_data(acpi_handle); extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32); extern int register_acpi_notifier(struct notifier_block *); extern int unregister_acpi_notifier(struct notifier_block *); +int acpi_dev_for_each_child(struct acpi_device *adev, + int (*fn)(struct device *, void *), void *data);
/* * External Functions
From: "Rafael J. Wysocki" rafael.j.wysocki@intel.com
mainline inclusion from mainline-v5.19-rc1 commit 10fa1b2cdc899ab471000968af56215bf3c90d8e category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I67QNJ CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?...
---------------------------------------------
When walking the children of an ACPI device, take extra care to avoid using to_acpi_device() on the ones that are not ACPI devices, because that may lead to out-of-bounds access and memory corruption.
While at it, make the function passed to acpi_dev_for_each_child() take a struct acpi_device pointer argument (instead of a struct device one), so it is more straightforward to use.
Fixes: b7dd6298db81 ("ACPI: PM: Introduce acpi_dev_power_up_children_with_adr()") Reported-by: kernel test robot oliver.sang@intel.com BugLink: https://lore.kernel.org/lkml/20220420064725.GB16310@xsang-OptiPlex-9020/ Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Reviewed-by: Mika Westerberg mika.westerberg@linux.intel.com Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- drivers/acpi/bus.c | 24 ++++++++++++++++++++++-- include/acpi/acpi_bus.h | 2 +- 2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index d8a6b32a17ad..de8185585f46 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -963,10 +963,30 @@ struct bus_type acpi_bus_type = { .uevent = acpi_device_uevent, };
+struct acpi_dev_walk_context { + int (*fn)(struct acpi_device *, void *); + void *data; +}; + +static int acpi_dev_for_one_check(struct device *dev, void *context) +{ + struct acpi_dev_walk_context *adwc = context; + + if (dev->bus != &acpi_bus_type) + return 0; + + return adwc->fn(to_acpi_device(dev), adwc->data); +} + int acpi_dev_for_each_child(struct acpi_device *adev, - int (*fn)(struct device *, void *), void *data) + int (*fn)(struct acpi_device *, void *), void *data) { - return device_for_each_child(&adev->dev, data, fn); + struct acpi_dev_walk_context adwc = { + .fn = fn, + .data = data, + }; + + return device_for_each_child(&adev->dev, &adwc, acpi_dev_for_one_check); }
/* -------------------------------------------------------------------------- diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index d16a181d8a0e..fc0693c1b87e 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -495,7 +495,7 @@ extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32); extern int register_acpi_notifier(struct notifier_block *); extern int unregister_acpi_notifier(struct notifier_block *); int acpi_dev_for_each_child(struct acpi_device *adev, - int (*fn)(struct device *, void *), void *data); + int (*fn)(struct acpi_device *, void *), void *data);
/* * External Functions
From: "Rafael J. Wysocki" rafael.j.wysocki@intel.com
mainline inclusion from mainline-v6.0-rc1 commit f8128c390e58928b16f197416d417cfa4c65f610 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I67QNJ CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?...
----------------------------------------------
Some pieces of modular code can benefit from using acpi_dev_for_each_child(), so export it to modules.
Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Reviewed-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- drivers/acpi/bus.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index de8185585f46..6ad227006283 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -988,6 +988,7 @@ int acpi_dev_for_each_child(struct acpi_device *adev,
return device_for_each_child(&adev->dev, &adwc, acpi_dev_for_one_check); } +EXPORT_SYMBOL_GPL(acpi_dev_for_each_child);
/* -------------------------------------------------------------------------- Initialization/Cleanup
Offering HULK hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I67QNJ CVE: NA
------------------------------------------
Export the containers in the system, drivers can take the advantage of these containers to manipulate the devices in the container.
Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- drivers/acpi/container.c | 51 +++++++++++++++++++++++++++++++++++++++ include/linux/container.h | 8 ++++++ 2 files changed, 59 insertions(+)
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index ccaa647ac3d4..fc9816b52537 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -11,6 +11,7 @@ */ #include <linux/acpi.h> #include <linux/container.h> +#include <linux/list.h>
#include "internal.h"
@@ -23,6 +24,52 @@ static const struct acpi_device_id container_device_ids[] = {
#ifdef CONFIG_ACPI_CONTAINER
+struct cdev_node *cdev_list; +EXPORT_SYMBOL_GPL(cdev_list); + +static void cdev_list_init(void) +{ + cdev_list = kmalloc(sizeof(struct cdev_node), GFP_KERNEL); + if (!cdev_list) { + pr_debug("container list init failed\n"); + return; + } + + INIT_LIST_HEAD(&cdev_list->clist); +} + +static void cdev_list_del_node(struct acpi_device *adev) +{ + struct cdev_node *cnode, *tmp; + + if (!cdev_list) + return; + + list_for_each_entry_safe(cnode, tmp, &cdev_list->clist, clist) { + if (cnode->dev == &adev->dev) { + list_del(&cnode->clist); + kfree(cnode); + } + } +} + +static void cdev_list_add_node(struct acpi_device *adev) +{ + struct cdev_node *cnode; + + if (!cdev_list) + return; + + cnode = kmalloc(sizeof(struct cdev_node), GFP_KERNEL); + if (!cnode) { + pr_debug("container list add cdev_node failed\n"); + return; + } + + cnode->dev = &adev->dev; + list_add_tail(&cnode->clist, &cdev_list->clist); +} + static int acpi_container_offline(struct container_dev *cdev) { struct acpi_device *adev = ACPI_COMPANION(&cdev->dev); @@ -67,6 +114,8 @@ static int container_device_attach(struct acpi_device *adev, return ret; } adev->driver_data = dev; + cdev_list_add_node(adev); + return 1; }
@@ -74,6 +123,7 @@ static void container_device_detach(struct acpi_device *adev) { struct device *dev = acpi_driver_data(adev);
+ cdev_list_del_node(adev); adev->driver_data = NULL; if (dev) device_unregister(dev); @@ -100,6 +150,7 @@ static struct acpi_scan_handler container_handler = { void __init acpi_container_init(void) { acpi_scan_add_handler(&container_handler); + cdev_list_init(); }
#else diff --git a/include/linux/container.h b/include/linux/container.h index 2566a1baa736..1f5fa722777f 100644 --- a/include/linux/container.h +++ b/include/linux/container.h @@ -24,4 +24,12 @@ static inline struct container_dev *to_container_dev(struct device *dev) return container_of(dev, struct container_dev, dev); }
+/* drivers/acpi/container.c */ +extern struct cdev_node *cdev_list; + +struct cdev_node { + struct device *dev; + struct list_head clist; +}; + #endif /* _LINUX_CONTAINER_H */
Offering HULK hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I67QNJ CVE: NA
---------------------------------------
This patch export the symbol of acpi_hotplug_schedule, and move the declaration of the acpi_hotplug_schedule in drivers/acpi/internal.h to include/linux/acpi.h. Drivers can use this function to online/offline the memory devices.
Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- drivers/acpi/internal.h | 1 - drivers/acpi/osl.c | 1 + include/linux/acpi.h | 1 + 3 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 125e4901c9b4..5ac34c0eeb47 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -82,7 +82,6 @@ static inline void acpi_lpss_init(void) {}
void acpi_apd_init(void);
-acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src); bool acpi_queue_hotplug_work(struct work_struct *work); void acpi_device_hotplug(struct acpi_device *adev, u32 src); bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 0418febc5cf2..b11c74f229a5 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1188,6 +1188,7 @@ acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src) } return AE_OK; } +EXPORT_SYMBOL_GPL(acpi_hotplug_schedule);
bool acpi_queue_hotplug_work(struct work_struct *work) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index b598dd608e7b..097225fedd94 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1405,5 +1405,6 @@ acpi_platform_notify(struct device *dev, enum kobject_action action) struct acpi_pptt_processor * acpi_pptt_find_cache_backwards(struct acpi_table_header *table_hdr, struct acpi_pptt_cache *cache); +acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src);
#endif /*_LINUX_ACPI_H*/
Offering: HULK hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I67QNJ CVE: NA
------------------------------------------------------------------
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: - state: Echo online to state to power up the devices in the container and then online these devices which will be triggered by BIOS. Echo offline to the state to 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 --- v4: - prettify the code
v3: - move the common code to hisi_internal.h
drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/hisilicon/Kconfig | 19 ++++ drivers/soc/hisilicon/Makefile | 3 + drivers/soc/hisilicon/hisi_hbmdev.c | 156 ++++++++++++++++++++++++++ drivers/soc/hisilicon/hisi_internal.h | 31 +++++ 6 files changed, 211 insertions(+) create mode 100644 drivers/soc/hisilicon/Kconfig create mode 100644 drivers/soc/hisilicon/Makefile create mode 100644 drivers/soc/hisilicon/hisi_hbmdev.c create mode 100644 drivers/soc/hisilicon/hisi_internal.h
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 425ab6f7e375..f7c59b063321 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -23,5 +23,6 @@ source "drivers/soc/versatile/Kconfig" source "drivers/soc/xilinx/Kconfig" source "drivers/soc/zte/Kconfig" source "drivers/soc/kendryte/Kconfig" +source "drivers/soc/hisilicon/Kconfig"
endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 36452bed86ef..68f186e00e44 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_PLAT_VERSATILE) += versatile/ obj-y += xilinx/ obj-$(CONFIG_ARCH_ZX) += zte/ obj-$(CONFIG_SOC_KENDRYTE) += kendryte/ +obj-y += hisilicon/ diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig new file mode 100644 index 000000000000..497787af004e --- /dev/null +++ b/drivers/soc/hisilicon/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Hisilicon SoC drivers +# +menu "Hisilicon SoC driver support" + +config HISI_HBMDEV + tristate "add extra support for hbm memory device" + depends on ACPI_HOTPLUG_MEMORY + select ACPI_CONTAINER + help + This driver add extra supports for memory devices. The driver + provides methods for userpace to control the power of memory + devices in a container. + + To compile this driver as a module, choose M here: + the module will be called hisi_hbmdev. + +endmenu diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile new file mode 100644 index 000000000000..22e87acb1ab3 --- /dev/null +++ b/drivers/soc/hisilicon/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_HISI_HBMDEV) += hisi_hbmdev.o diff --git a/drivers/soc/hisilicon/hisi_hbmdev.c b/drivers/soc/hisilicon/hisi_hbmdev.c new file mode 100644 index 000000000000..255c8014ec08 --- /dev/null +++ b/drivers/soc/hisilicon/hisi_hbmdev.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2023. All rights reserved. + */ + +#include <linux/kobject.h> +#include <linux/module.h> +#include <linux/nodemask.h> +#include <linux/acpi.h> +#include <linux/container.h> + +#include "hisi_internal.h" + +struct memory_dev { + struct kobject *memdev_kobj; +}; + +static struct memory_dev *mdev; + +static int get_pxm(struct acpi_device *acpi_device, void *arg) +{ + acpi_handle handle = acpi_device->handle; + nodemask_t *mask = arg; + unsigned long long sta; + acpi_status status; + int nid; + + 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) +{ + struct acpi_device *adev = to_acpi_device(dev); + nodemask_t mask; + + nodes_clear(mask); + acpi_dev_for_each_child(adev, get_pxm, &mask); + + return sysfs_emit(buf, "%*pbl\n", + nodemask_pr_args(&mask)); +} +static DEVICE_ATTR_RO(pxms); + +static int memdev_power_on(struct acpi_device *adev) +{ + acpi_handle handle = adev->handle; + acpi_status status; + + status = acpi_evaluate_object(handle, "_ON", NULL, NULL); + if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, "Power on failed (0x%x)\n", status); + return -ENODEV; + } + + return 0; +} + +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; + + get_device(&acpi_device->dev); + status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT); + if (ACPI_SUCCESS(status)) + return 0; + + put_device(&acpi_device->dev); + 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 int memdev_power_off(struct acpi_device *adev) +{ + return acpi_dev_for_each_child(adev, eject_device, NULL); +} + +static ssize_t state_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *adev = to_acpi_device(d); + const int type = online_type_from_str(buf); + int ret = -EINVAL; + + switch (type) { + case STATE_ONLINE: + ret = memdev_power_on(adev); + break; + case STATE_OFFLINE: + ret = memdev_power_off(adev); + break; + default: + break; + } + + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_WO(state); + +static int __init mdev_init(void) +{ + struct cdev_node *cnode; + + mdev = kzalloc(sizeof(struct memory_dev), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + + mdev->memdev_kobj = kobject_create_and_add("hbm_memory", kernel_kobj); + if (!mdev->memdev_kobj) { + kfree(mdev); + return -ENOMEM; + } + + list_for_each_entry(cnode, &cdev_list->clist, clist) { + device_create_file(cnode->dev, &dev_attr_state); + device_create_file(cnode->dev, &dev_attr_pxms); + } + + return 0; +} +module_init(mdev_init); + +static void __exit mdev_exit(void) +{ + struct cdev_node *cnode; + + list_for_each_entry(cnode, &cdev_list->clist, clist) { + device_remove_file(cnode->dev, &dev_attr_state); + device_remove_file(cnode->dev, &dev_attr_pxms); + } + + kobject_put(mdev->memdev_kobj); + kfree(mdev); +} +module_exit(mdev_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Zhang Zekun zhangzekun11@huawei.com"); diff --git a/drivers/soc/hisilicon/hisi_internal.h b/drivers/soc/hisilicon/hisi_internal.h new file mode 100644 index 000000000000..5345174f6b84 --- /dev/null +++ b/drivers/soc/hisilicon/hisi_internal.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2023. All rights reserved. + */ + +#ifndef _HISI_INTERNAL_H +#define _HISI_INTERNAL_H + +enum { + STATE_ONLINE, + STATE_OFFLINE, +}; + +static const char *const online_type_to_str[] = { + [STATE_ONLINE] = "online", + [STATE_OFFLINE] = "offline", +}; + +static inline int online_type_from_str(const char *str) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(online_type_to_str); i++) { + if (sysfs_streq(str, online_type_to_str[i])) + return i; + } + + return -EINVAL; +} + +#endif
Offering HULK hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I67QNJ CVE: NA
---------------------------------------------------------
Export the state of a hotplug memory device, and driviers can use this information to manipulate the hotplug memory device.
Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- drivers/acpi/acpi_memhotplug.c | 6 ++++++ include/linux/memory_hotplug.h | 2 ++ 2 files changed, 8 insertions(+)
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index b02fd51e5589..e9b9e2db32cf 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -56,6 +56,9 @@ struct acpi_memory_device { struct list_head res_list; };
+struct acpi_device *hotplug_mdev[MAX_NUMNODES]; +EXPORT_SYMBOL_GPL(hotplug_mdev); + static acpi_status acpi_memory_get_resource(struct acpi_resource *resource, void *context) { @@ -217,6 +220,8 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) * Add num_enable even if add_memory() returns -EEXIST, so the * device is bound to this driver. */ + + hotplug_mdev[node] = mem_device->device; num_enabled++; } if (!num_enabled) { @@ -240,6 +245,7 @@ static void acpi_memory_remove_memory(struct acpi_memory_device *mem_device) struct acpi_memory_info *info, *n; int nid = acpi_get_node(handle);
+ hotplug_mdev[nid] = NULL; list_for_each_entry_safe(info, n, &mem_device->res_list, list) { if (!info->enabled) continue; diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index b9aeabcce49a..3debe1f7a6c1 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -316,6 +316,8 @@ extern void set_zone_contiguous(struct zone *zone); extern void clear_zone_contiguous(struct zone *zone);
#ifdef CONFIG_MEMORY_HOTPLUG +extern struct acpi_device *hotplug_mdev[MAX_NUMNODES]; + extern void __ref free_area_init_core_hotplug(int nid); extern int __add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags); extern int add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags);
Offering: HULK hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I67QNJ CVE: NA
--------------------------------------------------
Provide additional memory device topology information in hisi_hbmdev. This memory topology informaton can provide useful information for userspace to select the closest memory device to a certain cpu.
Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- v4: - prettify the code
drivers/soc/hisilicon/Kconfig | 9 +++-- drivers/soc/hisilicon/hisi_hbmdev.c | 62 +++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig index 497787af004e..781e97b1b742 100644 --- a/drivers/soc/hisilicon/Kconfig +++ b/drivers/soc/hisilicon/Kconfig @@ -9,9 +9,12 @@ config HISI_HBMDEV depends on ACPI_HOTPLUG_MEMORY select ACPI_CONTAINER help - This driver add extra supports for memory devices. The driver - provides methods for userpace to control the power of memory - devices in a container. + This driver add two extra supports for memory devices. The driver + provides methods for userpace to control the power of memory devices + in a container. Besides, it provides extra locality information + between cpus and memory devices for userspace, which can take + advantage of this functionality to select the closet memory device + to a certain cpu.
To compile this driver as a module, choose M here: the module will be called hisi_hbmdev. diff --git a/drivers/soc/hisilicon/hisi_hbmdev.c b/drivers/soc/hisilicon/hisi_hbmdev.c index 255c8014ec08..09dea1d0b270 100644 --- a/drivers/soc/hisilicon/hisi_hbmdev.c +++ b/drivers/soc/hisilicon/hisi_hbmdev.c @@ -8,15 +8,74 @@ #include <linux/nodemask.h> #include <linux/acpi.h> #include <linux/container.h> +#include <linux/node.h> +#include <linux/arch_topology.h> +#include <linux/memory_hotplug.h>
#include "hisi_internal.h"
struct memory_dev { struct kobject *memdev_kobj; + struct kobject *topo_kobj; + nodemask_t cluster_cpumask[MAX_NUMNODES]; };
static struct memory_dev *mdev;
+static ssize_t memory_locality_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + int i, count = 0; + + for (i = 0; i < MAX_NUMNODES; i++) { + if (hotplug_mdev[i] != NULL && !nodes_empty(mdev->cluster_cpumask[i])) { + count += sysfs_emit_at(buf, count, "%d %*pbl\n", i, + nodemask_pr_args(&mdev->cluster_cpumask[i])); + } + } + + return count; +} + +static struct kobj_attribute memory_locality_attribute = + __ATTR(memory_locality, 0444, memory_locality_show, NULL); + +static void memory_topo_init(void) +{ + int ret, nid, cluster_id, cpu; + struct acpi_device *adev; + nodemask_t mask; + + for (nid = 0; nid < MAX_NUMNODES; nid++) { + if (!hotplug_mdev[nid]) + continue; + + adev = hotplug_mdev[nid]; + ret = fwnode_property_read_u32(acpi_fwnode_handle(adev), + "cluster-id", &cluster_id); + if (ret < 0) { + pr_debug("Failed to read cluster id\n"); + return; + } + + nodes_clear(mask); + for_each_possible_cpu(cpu) { + if (topology_cluster_id(cpu) == cluster_id) + node_set(cpu, mask); + } + mdev->cluster_cpumask[nid] = mask; + } + + mdev->topo_kobj = kobject_create_and_add("memory_topo", mdev->memdev_kobj); + if (!mdev->topo_kobj) + return; + + ret = sysfs_create_file(mdev->topo_kobj, &memory_locality_attribute.attr); + if (ret) + kobject_put(mdev->topo_kobj); +} + static int get_pxm(struct acpi_device *acpi_device, void *arg) { acpi_handle handle = acpi_device->handle; @@ -134,6 +193,8 @@ static int __init mdev_init(void) device_create_file(cnode->dev, &dev_attr_pxms); }
+ memory_topo_init(); + return 0; } module_init(mdev_init); @@ -148,6 +209,7 @@ static void __exit mdev_exit(void) }
kobject_put(mdev->memdev_kobj); + kobject_put(mdev->topo_kobj); kfree(mdev); } module_exit(mdev_exit);
Offering HULK hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I6I7DH CVE: NA
--------------------------------------------------
Add a driver for hbm cache, which support for power on/off the hbm cache device. Hbm can be used as a cache, for which a normal memory access can take advantage of the high band width of hbm.
Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- v3: - prettify the code - move the common code to hisi_internal.h
v2: - remove the !adev judge, as it will always be true.
drivers/soc/hisilicon/Kconfig | 11 ++ drivers/soc/hisilicon/Makefile | 1 + drivers/soc/hisilicon/hisi_hbmcache.c | 147 ++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 drivers/soc/hisilicon/hisi_hbmcache.c
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig index 781e97b1b742..160fdadd76be 100644 --- a/drivers/soc/hisilicon/Kconfig +++ b/drivers/soc/hisilicon/Kconfig @@ -19,4 +19,15 @@ config HISI_HBMDEV To compile this driver as a module, choose M here: the module will be called hisi_hbmdev.
+config HISI_HBMCACHE + tristate "HBM cache memory device" + depends on ACPI + help + This driver provids methods to control the power of hbm cache device + in hisi soc. Use hbm as a cache can take advantage of hbm's high + bandwidth in normal memory access. + + To compile the driver as a module, choose M here: + the module will be called hisi_hbmcache. + endmenu diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile index 22e87acb1ab3..5a1ed2d53593 100644 --- a/drivers/soc/hisilicon/Makefile +++ b/drivers/soc/hisilicon/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_HISI_HBMDEV) += hisi_hbmdev.o +obj-$(CONFIG_HISI_HBMCACHE) += hisi_hbmcache.o diff --git a/drivers/soc/hisilicon/hisi_hbmcache.c b/drivers/soc/hisilicon/hisi_hbmcache.c new file mode 100644 index 000000000000..34121320742e --- /dev/null +++ b/drivers/soc/hisilicon/hisi_hbmcache.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2023. All rights reserved. + */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/device.h> + +#include "hisi_internal.h" + +#define MODULE_NAME "hbm_cache" + +static struct kobject *cache_kobj; + +static ssize_t state_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *adev = ACPI_COMPANION(d); + const int type = online_type_from_str(buf); + int ret = -EINVAL; + + switch (type) { + case STATE_ONLINE: + ret = acpi_device_set_power(adev, ACPI_STATE_D0); + break; + case STATE_OFFLINE: + ret = acpi_device_set_power(adev, ACPI_STATE_D3); + break; + default: + break; + } + + if (ret) + return ret; + + return count; +} + +static ssize_t state_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct acpi_device *adev = ACPI_COMPANION(d); + unsigned long long sta = 0; + acpi_status status; + const char *output; + + status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) + return -EINVAL; + + output = (sta & 0x01) ? online_type_to_str[STATE_ONLINE] : + online_type_to_str[STATE_OFFLINE]; + + return sysfs_emit(buf, "%s\n", output); +} +static DEVICE_ATTR_RW(state); + +static ssize_t socket_id_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + int socket_id; + + if (device_property_read_u32(d, "socket_id", &socket_id)) + return -EINVAL; + + return sysfs_emit(buf, "%d\n", socket_id); +} +static DEVICE_ATTR_RO(socket_id); + +static struct attribute *attrs[] = { + &dev_attr_state.attr, + &dev_attr_socket_id.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = attrs, +}; + +static int cache_probe(struct platform_device *pdev) +{ + int ret; + + ret = sysfs_create_group(&pdev->dev.kobj, &attr_group); + if (ret) + return ret; + + ret = sysfs_create_link(cache_kobj, + &pdev->dev.kobj, + kobject_name(&pdev->dev.kobj)); + if (ret) { + sysfs_remove_group(&pdev->dev.kobj, &attr_group); + return ret; + } + + return 0; +} + +static int cache_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &attr_group); + sysfs_remove_link(&pdev->dev.kobj, + kobject_name(&pdev->dev.kobj)); + return 0; +} + +static const struct acpi_device_id cache_acpi_ids[] = { + {"HISI04A1"}, + {}, +}; + +static struct platform_driver hbm_cache_driver = { + .probe = cache_probe, + .remove = cache_remove, + .driver = { + .name = MODULE_NAME, + .acpi_match_table = ACPI_PTR(cache_acpi_ids), + }, +}; + +static int __init hbm_cache_module_init(void) +{ + int ret; + + cache_kobj = kobject_create_and_add("hbm_cache", kernel_kobj); + if (!cache_kobj) + return -ENOMEM; + + ret = platform_driver_register(&hbm_cache_driver); + if (ret) { + kobject_put(cache_kobj); + return ret; + } + return 0; +} +module_init(hbm_cache_module_init); + +static void __exit hbm_cache_module_exit(void) +{ + kobject_put(cache_kobj); + platform_driver_unregister(&hbm_cache_driver); +} +module_exit(hbm_cache_module_exit); +MODULE_LICENSE("GPL");