未验证 提交 6b10ee06 编写于 作者: O openeuler-ci-bot 提交者: Gitee

!451 add drivers to support hbm memory and hbm cache

Merge Pull Request from: @zhang-zekun-zk 
 
add drivers to support hbm memory and hbm cache 
 
Link:https://gitee.com/openeuler/kernel/pulls/451 

Reviewed-by: Jialin Zhang <zhangjialin11@huawei.com> 
Reviewed-by: Kefeng Wang <wangkefeng.wang@huawei.com> 
Signed-off-by: Jialin Zhang <zhangjialin11@huawei.com> 
......@@ -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;
......
......@@ -966,6 +966,33 @@ 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 acpi_device *, void *), void *data)
{
struct acpi_dev_walk_context adwc = {
.fn = fn,
.data = data,
};
return device_for_each_child(&adev->dev, &adwc, acpi_dev_for_one_check);
}
EXPORT_SYMBOL_GPL(acpi_dev_for_each_child);
/* --------------------------------------------------------------------------
Initialization/Cleanup
-------------------------------------------------------------------------- */
......
......@@ -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);
......
......@@ -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)
{
......
......@@ -30,6 +30,7 @@ struct bus_type container_subsys = {
.online = trivial_online,
.offline = container_offline,
};
EXPORT_SYMBOL_GPL(container_subsys);
void __init container_dev_init(void)
{
......
......@@ -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
......@@ -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/
# 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 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.
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
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_HISI_HBMDEV) += hisi_hbmdev.o
obj-$(CONFIG_HISI_HBMCACHE) += hisi_hbmcache.o
// 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");
// 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 <linux/node.h>
#include <linux/arch_topology.h>
#include <linux/memory_hotplug.h>
#include "hisi_internal.h"
#define ACPI_MEMORY_DEVICE_HID "PNP0C80"
#define ACPI_GENERIC_CONTAINER_DEVICE_HID "PNP0A06"
struct cdev_node {
struct device *dev;
struct list_head clist;
};
struct memory_dev {
struct kobject *memdev_kobj;
struct kobject *topo_kobj;
struct cdev_node cdev_list;
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;
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 = ACPI_COMPANION(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 *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
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 hbmdev_find(struct acpi_device *adev, void *arg)
{
const char *hid = acpi_device_hid(adev);
bool *found = arg;
if (!strcmp(hid, ACPI_MEMORY_DEVICE_HID)) {
*found = true;
return -1;
}
return 0;
}
static bool has_hbmdev(struct device *dev)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
const char *hid = acpi_device_hid(adev);
bool found = false;
if (strcmp(hid, ACPI_GENERIC_CONTAINER_DEVICE_HID))
return found;
acpi_dev_for_each_child(adev, hbmdev_find, &found);
return found;
}
static int container_add(struct device *dev, void *data)
{
struct cdev_node *cnode;
if (!has_hbmdev(dev))
return 0;
cnode = kmalloc(sizeof(struct cdev_node), GFP_KERNEL);
if (!cnode)
return -ENOMEM;
cnode->dev = dev;
list_add_tail(&cnode->clist, &mdev->cdev_list.clist);
return 0;
}
static void container_remove(void)
{
struct cdev_node *cnode, *tmp;
list_for_each_entry_safe(cnode, tmp, &mdev->cdev_list.clist, clist) {
device_remove_file(cnode->dev, &dev_attr_state);
device_remove_file(cnode->dev, &dev_attr_pxms);
list_del(&cnode->clist);
kfree(cnode);
}
}
static int container_init(void)
{
struct cdev_node *cnode;
INIT_LIST_HEAD(&mdev->cdev_list.clist);
if (bus_for_each_dev(&container_subsys, NULL, NULL, container_add)) {
container_remove();
return -ENOMEM;
}
if (list_empty(&mdev->cdev_list.clist))
return -ENODEV;
list_for_each_entry(cnode, &mdev->cdev_list.clist, clist) {
device_create_file(cnode->dev, &dev_attr_state);
device_create_file(cnode->dev, &dev_attr_pxms);
}
return 0;
}
static int __init mdev_init(void)
{
int ret;
mdev = kzalloc(sizeof(struct memory_dev), GFP_KERNEL);
if (!mdev)
return -ENOMEM;
ret = container_init();
if (ret) {
kfree(mdev);
return ret;
}
mdev->memdev_kobj = kobject_create_and_add("hbm_memory", kernel_kobj);
if (!mdev->memdev_kobj) {
container_remove();
kfree(mdev);
return -ENOMEM;
}
memory_topo_init();
return ret;
}
module_init(mdev_init);
static void __exit mdev_exit(void)
{
container_remove();
kobject_put(mdev->memdev_kobj);
kobject_put(mdev->topo_kobj);
kfree(mdev);
}
module_exit(mdev_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Zhang Zekun <zhangzekun11@huawei.com>");
/* 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
......@@ -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 acpi_device *, void *), void *data);
/*
* External Functions
......
......@@ -1407,5 +1407,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*/
......@@ -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);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册