* [PATCH 0/2] soc cache: Add support for HiSilicon L3 cache
@ 2025-01-07 13:29 Yushan Wang
2025-01-07 13:29 ` [PATCH 1/2] soc cache: Add framework driver for HiSilicon SoC cache Yushan Wang
2025-01-07 13:29 ` [PATCH 2/2] soc cache: L3 cache lockdown support for HiSilicon SoC Yushan Wang
0 siblings, 2 replies; 8+ messages in thread
From: Yushan Wang @ 2025-01-07 13:29 UTC (permalink / raw)
To: xuwei5, yangyicong, Jonathan.Cameron, wangjie125, linux-kernel
Cc: prime.zeng, fanghao11, wangyushan12, linuxarm
This series adds support for HiSilicon SoC cache lockdown and cache
maintenance operations.
Cache lockdown feature prevents cache entries from being evicted from L3
cache for better performance. It can be enabled by calling mmap
function to the file (`/dev/hisi_soc_cache_mgmt`). This feature is
implemented in the driver hisi_soc_l3c.
L3 cache and L3 cache PMU share the same memory resource, which makes
one fails to probe while another is on board. Since both devices
rely on distinct information exported by ACPI, their probing functions
should be unrelated. Workaround the resource conflict check by
replacing devm_ioremap_resource() to devm_ioremap() instead.
Jie Wang (1):
soc cache: Add framework driver for HiSilicon SoC cache
Yushan Wang (1):
soc cache: L3 cache lockdown support for HiSilicon SoC
drivers/soc/hisilicon/Kconfig | 22 +
drivers/soc/hisilicon/Makefile | 2 +
.../soc/hisilicon/hisi_soc_cache_framework.c | 543 ++++++++++++++++++
.../soc/hisilicon/hisi_soc_cache_framework.h | 77 +++
drivers/soc/hisilicon/hisi_soc_l3c.c | 540 +++++++++++++++++
5 files changed, 1184 insertions(+)
create mode 100644 drivers/soc/hisilicon/hisi_soc_cache_framework.c
create mode 100644 drivers/soc/hisilicon/hisi_soc_cache_framework.h
create mode 100644 drivers/soc/hisilicon/hisi_soc_l3c.c
--
2.33.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/2] soc cache: Add framework driver for HiSilicon SoC cache
2025-01-07 13:29 [PATCH 0/2] soc cache: Add support for HiSilicon L3 cache Yushan Wang
@ 2025-01-07 13:29 ` Yushan Wang
2025-01-07 19:05 ` Christophe JAILLET
2025-01-08 13:54 ` kernel test robot
2025-01-07 13:29 ` [PATCH 2/2] soc cache: L3 cache lockdown support for HiSilicon SoC Yushan Wang
1 sibling, 2 replies; 8+ messages in thread
From: Yushan Wang @ 2025-01-07 13:29 UTC (permalink / raw)
To: xuwei5, yangyicong, Jonathan.Cameron, wangjie125, linux-kernel
Cc: prime.zeng, fanghao11, wangyushan12, linuxarm
From: Jie Wang <wangjie125@huawei.com>
HiSilicon SoC cache is comprised of multiple hardware devices, a driver
in this patch is used to provide common utilities for other drivers to
avoid redundancy.
The driver will create a file of `/dev/hisi_soc_cache_mgmt`, mmap
operations to it will be taken as applying cache lock for a memory
region, ioctl operations will be taken as cache maintenance.
The address is aligned up to return to user and the arena's starting
address is stored for future release. Unused pages produced during
above process are released to allow minimum arena.
Co-developed-by: Yicong Yang <yangyicong@hisilicon.com>
Signed-off-by: Yicong Yang <yangyicong@hisilicon.com>
Co-developed-by: Yushan Wang <wangyushan12@huawei.com>
Signed-off-by: Yushan Wang <wangyushan12@huawei.com>
Signed-off-by: Jie Wang <wangjie125@huawei.com>
---
drivers/soc/hisilicon/Kconfig | 11 +
drivers/soc/hisilicon/Makefile | 1 +
.../soc/hisilicon/hisi_soc_cache_framework.c | 543 ++++++++++++++++++
.../soc/hisilicon/hisi_soc_cache_framework.h | 77 +++
4 files changed, 632 insertions(+)
create mode 100644 drivers/soc/hisilicon/hisi_soc_cache_framework.c
create mode 100644 drivers/soc/hisilicon/hisi_soc_cache_framework.h
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
index 6d7c244d2e78..ef826c895a9d 100644
--- a/drivers/soc/hisilicon/Kconfig
+++ b/drivers/soc/hisilicon/Kconfig
@@ -21,4 +21,15 @@ config KUNPENG_HCCS
health status and port information of HCCS, or reducing system
power consumption on Kunpeng SoC.
+config HISI_SOC_CACHE
+ bool "HiSilicon Cache driver for Kunpeng SoC"
+ depends on ARCH_HISI
+ help
+ This driver provides the basic utilities for drivers of
+ different part of Kunpeng SoC cache, including L3 cache and
+ Hydra Home Agent etc.
+
+ If either HiSilicon L3 cache driver or HiSilicon Hydra Home
+ Agent driver is needed, say yes.
+
endmenu
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
index 226e747e70d6..dbc673b71d4d 100644
--- a/drivers/soc/hisilicon/Makefile
+++ b/drivers/soc/hisilicon/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_KUNPENG_HCCS) += kunpeng_hccs.o
+obj-$(CONFIG_HISI_SOC_CACHE) += hisi_soc_cache_framework.o
diff --git a/drivers/soc/hisilicon/hisi_soc_cache_framework.c b/drivers/soc/hisilicon/hisi_soc_cache_framework.c
new file mode 100644
index 000000000000..f87dc155c778
--- /dev/null
+++ b/drivers/soc/hisilicon/hisi_soc_cache_framework.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Framework for HiSilicon SoC cache, manages HiSilicon SoC cache drivers.
+ *
+ * Copyright (c) 2024 HiSilicon Technologies Co., Ltd.
+ * Author: Jie Wang <wangjie125@huawei.com>
+ * Author: Yicong Yang <yangyicong@hisilicon.com>
+ * Author: Yushan Wang <wangyushan12@huawei.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cleanup.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/memory.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/pagewalk.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+#include <asm/page.h>
+
+#include "hisi_soc_cache_framework.h"
+
+struct hisi_soc_cache_lock_region {
+ /* physical memory region of the arena allocated for aligned address */
+ unsigned long arena_start;
+ unsigned long arena_end;
+ /* VMA region of locked memory for future release */
+ unsigned long vm_start;
+ unsigned long vm_end;
+ phys_addr_t addr;
+ size_t size;
+ u32 status;
+ int cpu;
+};
+
+struct hisi_soc_comp_inst {
+ struct list_head node;
+ struct hisi_soc_comp *comp;
+};
+
+struct hisi_soc_comp_list {
+ struct list_head node;
+ /* protects list of HiSilicon SoC cache components */
+ spinlock_t lock;
+ u32 inst_num;
+};
+
+static struct hisi_soc_comp_list soc_cache_devs[SOC_COMP_TYPE_MAX];
+
+static int hisi_soc_cache_lock(int cpu, phys_addr_t addr, size_t size)
+{
+ struct hisi_soc_comp_inst *inst;
+ struct list_head *head;
+ int ret = -ENOMEM;
+
+ guard(spinlock)(&soc_cache_devs[HISI_SOC_L3C].lock);
+
+ /* Iterate L3C instances to perform operation, break loop once found. */
+ head = &soc_cache_devs[HISI_SOC_L3C].node;
+ list_for_each_entry(inst, head, node) {
+ if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
+ continue;
+ ret = inst->comp->ops->do_lock(inst->comp, addr, size);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ list_for_each_entry(inst, head, node) {
+ if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
+ continue;
+ ret = inst->comp->ops->poll_lock_done(inst->comp, addr, size);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ return ret;
+}
+
+static int hisi_soc_cache_unlock(int cpu, phys_addr_t addr)
+{
+ struct hisi_soc_comp_inst *inst;
+ struct list_head *head;
+ int ret = 0;
+
+ guard(spinlock)(&soc_cache_devs[HISI_SOC_L3C].lock);
+
+ /* Iterate L3C instances to perform operation, break loop once found. */
+ head = &soc_cache_devs[HISI_SOC_L3C].node;
+ list_for_each_entry(inst, head, node) {
+ if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
+ continue;
+ ret = inst->comp->ops->do_unlock(inst->comp, addr);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ list_for_each_entry(inst, head, node) {
+ if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
+ continue;
+ ret = inst->comp->ops->poll_unlock_done(inst->comp, addr);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ return ret;
+}
+
+static int hisi_soc_cache_inst_check(const struct hisi_soc_comp *comp,
+ enum hisi_soc_comp_type comp_type)
+{
+ struct hisi_soc_comp_ops *ops = comp->ops;
+
+ /* Different types of component could have different ops. */
+ switch (comp_type) {
+ case HISI_SOC_L3C:
+ if (!ops->do_lock || !ops->poll_lock_done
+ || !ops->do_unlock || !ops->poll_unlock_done)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hisi_soc_cache_inst_add(struct hisi_soc_comp *comp,
+ enum hisi_soc_comp_type comp_type)
+{
+ struct hisi_soc_comp_inst *comp_inst;
+ int ret;
+
+ ret = hisi_soc_cache_inst_check(comp, comp_type);
+ if (ret)
+ return ret;
+
+ comp_inst = kzalloc(sizeof(*comp_inst), GFP_KERNEL);
+ if (!comp_inst)
+ return -ENOMEM;
+
+ comp_inst->comp = comp;
+
+ scoped_guard(spinlock, &soc_cache_devs[comp_type].lock) {
+ list_add_tail(&comp_inst->node,
+ &soc_cache_devs[comp_type].node);
+ soc_cache_devs[comp_type].inst_num++;
+ }
+
+ return 0;
+}
+
+/*
+ * When @comp is NULL, it means to delete all instances of @comp_type.
+ */
+static void hisi_soc_cache_inst_del(struct hisi_soc_comp *comp,
+ enum hisi_soc_comp_type comp_type)
+{
+ struct hisi_soc_comp_inst *inst, *tmp;
+
+ guard(spinlock)(&soc_cache_devs[comp_type].lock);
+ list_for_each_entry_safe(inst, tmp, &soc_cache_devs[comp_type].node,
+ node) {
+ if (comp && comp != inst->comp)
+ continue;
+
+ if (soc_cache_devs[comp_type].inst_num > 0)
+ soc_cache_devs[comp_type].inst_num--;
+
+ list_del(&inst->node);
+ kfree(inst);
+
+ /* Stop the loop if we have already deleted @comp. */
+ if (comp)
+ break;
+ }
+}
+
+int hisi_soc_comp_inst_add(struct hisi_soc_comp *comp)
+{
+ int ret, i = HISI_SOC_L3C;
+
+ if (!comp || !comp->ops || comp->comp_type == 0)
+ return -EINVAL;
+
+ for_each_set_bit_from(i, &comp->comp_type, SOC_COMP_TYPE_MAX) {
+ ret = hisi_soc_cache_inst_add(comp, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hisi_soc_comp_inst_add);
+
+int hisi_soc_comp_inst_del(struct hisi_soc_comp *comp)
+{
+ int i;
+
+ if (!comp)
+ return -EINVAL;
+
+ for_each_set_bit(i, &comp->comp_type, SOC_COMP_TYPE_MAX)
+ hisi_soc_cache_inst_del(comp, i);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hisi_soc_comp_inst_del);
+
+static void hisi_soc_cache_vm_open(struct vm_area_struct *vma)
+{
+ struct hisi_soc_cache_lock_region *clr = vma->vm_private_data;
+
+ /*
+ * Only perform cache lock when the vma passed in is created
+ * in hisi_soc_cache_mmap.
+ */
+ if (clr->vm_start != vma->vm_start || clr->vm_end != vma->vm_end)
+ return;
+
+ clr->status = hisi_soc_cache_lock(clr->cpu, clr->addr, clr->size);
+}
+
+/**
+ * hisi_soc_cache_aligned_alloc - Allocate memory region to be locked and
+ * returns address that aligned to the requested
+ * size.
+ * @clr: The locked memory region to be allocated for.
+ * @size: Requested memory size.
+ * @addr: Pointer of the start physical address of the requested
+ * memory region.
+ *
+ * @return:
+ * - -ENOMEM: If allocation fails.
+ * - 0: If allocations succeeds.
+ *
+ * Physical address of allocated memory region is requested to be aligned to
+ * its size. In order to achieve that, add the order of requested memory size
+ * by 1 to double the size of allocated memory to ensure the existence of size-
+ * aligned address. After locating the aligned region, release the unused
+ * pages from both sides to avoid waste.
+ */
+static int hisi_soc_cache_aligned_alloc(struct hisi_soc_cache_lock_region *clr,
+ unsigned long size,
+ unsigned long *addr)
+{
+ unsigned long prefix_hole_size, suffix_hole_size;
+ unsigned long arena_start, arena_end;
+ int order = get_order(size) + 1;
+ unsigned long start, end;
+ unsigned long free_size;
+ struct page *pg;
+ int bit;
+
+ pg = alloc_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO, order);
+ if (!pg)
+ return -ENOMEM;
+
+ arena_start = page_to_phys(pg);
+ arena_end = arena_start + (PAGE_SIZE << order) - 1;
+
+ /*
+ * Align up the address by the requested size if the address is not
+ * naturally aligned to the size.
+ */
+ start = arena_start % size == 0
+ ? arena_start
+ : arena_start / size * size + size;
+ end = start + size - 1;
+ prefix_hole_size = start - arena_start;
+ suffix_hole_size = arena_end - end;
+
+ if (prefix_hole_size >= PAGE_SIZE) {
+ bit = PAGE_SHIFT;
+ for_each_set_bit_from(bit, &prefix_hole_size,
+ MAX_PAGE_ORDER + PAGE_SHIFT) {
+ free_size = (PAGE_SIZE << (bit - PAGE_SHIFT)) - 1;
+ __free_pages(phys_to_page(arena_start),
+ bit - PAGE_SHIFT);
+ arena_start += free_size + 1;
+ }
+ }
+
+ if (suffix_hole_size >= PAGE_SIZE) {
+ bit = PAGE_SHIFT;
+ for_each_set_bit_from(bit, &suffix_hole_size,
+ MAX_PAGE_ORDER + PAGE_SHIFT) {
+ free_size = (PAGE_SIZE << (bit - PAGE_SHIFT)) - 1;
+ __free_pages(phys_to_page(arena_end - free_size),
+ bit - PAGE_SHIFT);
+ arena_end -= free_size + 1;
+ }
+ }
+
+ *addr = start;
+ clr->arena_start = arena_start;
+ clr->arena_end = arena_end;
+
+ return 0;
+}
+
+/**
+ * hisi_soc_cache_aligned_free - Free the aligned memory region allcated by
+ * hisi_soc_cache_aligned_alloc().
+ * @clr: The allocated locked memory region.
+ *
+ * Since unused memory pages are release in hisi_soc_cache_aligned_alloc(), the
+ * memory region to be freed here may not be power of 2 numbers of pages.
+ * Thus split the memory by page order and release them accordingly.
+ */
+static void hisi_soc_cache_aligned_free(struct hisi_soc_cache_lock_region *clr)
+{
+ unsigned long size = clr->arena_end - clr->arena_start;
+ int bit = PAGE_SHIFT;
+
+ for_each_set_bit_from(bit, &size, MAX_PAGE_ORDER)
+ __free_pages(phys_to_page(clr->arena_start), bit - PAGE_SHIFT);
+}
+
+static void hisi_soc_cache_vm_close(struct vm_area_struct *vma)
+{
+ struct hisi_soc_cache_lock_region *clr = vma->vm_private_data;
+ int ret;
+
+ /*
+ * Only perform cache unlock when the vma passed in is created
+ * in hisi_soc_cache_mmap.
+ */
+ if (clr->vm_start != vma->vm_start || clr->vm_end != vma->vm_end)
+ return;
+
+ ret = hisi_soc_cache_unlock(clr->cpu, clr->addr);
+
+ hisi_soc_cache_aligned_free(clr);
+ kfree(clr);
+ vma->vm_private_data = NULL;
+}
+
+/*
+ * mremap operation is not supported for HiSilicon SoC cache.
+ */
+static int hisi_soc_cache_vm_mremap(struct vm_area_struct *vma)
+{
+ struct hisi_soc_cache_lock_region *clr = vma->vm_private_data;
+
+ /*
+ * vma region size will be changed as requested by mremap despite the
+ * callback failure in this function. Thus, change the vma region
+ * stored in clr according to the parameters to verify if the pages
+ * should be freed when unmapping.
+ */
+ clr->vm_end = clr->vm_start + (vma->vm_end - vma->vm_start);
+ pr_err("mremap for HiSilicon SoC locked cache is not supported\n");
+
+ return -EOPNOTSUPP;
+}
+
+static const struct vm_operations_struct hisi_soc_cache_vm_ops = {
+ .open = hisi_soc_cache_vm_open,
+ .close = hisi_soc_cache_vm_close,
+ .mremap = hisi_soc_cache_vm_mremap,
+};
+
+static int hisi_soc_cache_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long size = vma->vm_end - vma->vm_start;
+ struct hisi_soc_cache_lock_region *clr;
+ unsigned long addr;
+ int ret;
+
+ clr = kzalloc(sizeof(*clr), GFP_KERNEL);
+ if (!clr)
+ return -ENOMEM;
+
+ ret = hisi_soc_cache_aligned_alloc(clr, size, &addr);
+ if (ret)
+ goto out_clr;
+
+ ret = remap_pfn_range(vma, vma->vm_start,
+ (addr >> PAGE_SHIFT) + vma->vm_pgoff,
+ size, vma->vm_page_prot);
+ if (ret)
+ goto out_page;
+
+ clr->addr = addr;
+ clr->size = size;
+ clr->cpu = smp_processor_id();
+ vm_flags_set(vma, VM_DONTCOPY | VM_DONTEXPAND);
+
+ /*
+ * The vma should not be moved throughout its lifetime, store the
+ * region for verification.
+ */
+ clr->vm_start = vma->vm_start;
+ clr->vm_end = vma->vm_end;
+
+ vma->vm_private_data = clr;
+ vma->vm_ops = &hisi_soc_cache_vm_ops;
+ hisi_soc_cache_vm_ops.open(vma);
+
+ if (clr->status) {
+ ret = -ENOMEM;
+ goto out_page;
+ }
+
+ return 0;
+
+out_page:
+ hisi_soc_cache_aligned_free(clr);
+out_clr:
+ kfree(clr);
+ return ret;
+}
+
+static const struct file_operations soc_cache_dev_fops = {
+ .owner = THIS_MODULE,
+ .mmap = hisi_soc_cache_mmap,
+};
+
+static struct miscdevice soc_cache_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "hisi_soc_cache_mgmt",
+ .fops = &soc_cache_dev_fops,
+ .mode = 0600,
+};
+
+static void hisi_soc_cache_inst_uninit(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(soc_cache_devs); ++i)
+ hisi_soc_cache_inst_del(NULL, i);
+}
+
+static void hisi_soc_cache_framework_data_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(soc_cache_devs); ++i) {
+ spin_lock_init(&soc_cache_devs[i].lock);
+ INIT_LIST_HEAD(&soc_cache_devs[i].node);
+ }
+}
+
+static const char *const hisi_soc_cache_item_str[SOC_COMP_TYPE_MAX] = {
+ "cache"
+};
+
+/*
+ * Print cache instance number debug information for debug FS.
+ */
+static ssize_t hisi_soc_cache_dbg_get_inst_num(struct file *file,
+ char __user *buff,
+ size_t cnt,
+ loff_t *ppos)
+{
+#define HISI_SOC_CACHE_DBGFS_REG_LEN 100
+ char *read_buff __free(kfree) = NULL;
+ int len, i, pos = 0;
+
+ if (!access_ok(buff, cnt))
+ return -EFAULT;
+ if (*ppos < 0)
+ return -EINVAL;
+ if (cnt == 0)
+ return 0;
+
+ read_buff = kzalloc(HISI_SOC_CACHE_DBGFS_REG_LEN, GFP_KERNEL);
+ if (!read_buff)
+ return -ENOMEM;
+
+ len = HISI_SOC_CACHE_DBGFS_REG_LEN;
+
+ for (i = 0; i < ARRAY_SIZE(soc_cache_devs); i++) {
+ guard(spinlock)(&soc_cache_devs[i].lock);
+ pos += scnprintf(read_buff + pos, len - pos,
+ "%s inst num: %u\n",
+ hisi_soc_cache_item_str[i],
+ soc_cache_devs[i].inst_num);
+ }
+
+ return simple_read_from_buffer(buff, cnt, ppos, read_buff,
+ strlen(read_buff));
+}
+
+static struct dentry *hisi_cache_dbgfs_root;
+static const struct file_operations hisi_cache_dbgfs_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = hisi_soc_cache_dbg_get_inst_num,
+};
+
+static void hisi_soc_cache_dbgfs_init(void)
+{
+ hisi_cache_dbgfs_root = debugfs_create_dir("hisi_soc_cache_frm", NULL);
+ debugfs_create_file("instance", 0400, hisi_cache_dbgfs_root, NULL,
+ &hisi_cache_dbgfs_ops);
+}
+
+static void hisi_soc_cache_dbgfs_uninit(void)
+{
+ debugfs_remove_recursive(hisi_cache_dbgfs_root);
+ hisi_cache_dbgfs_root = NULL;
+}
+
+static int __init hisi_soc_cache_framework_init(void)
+{
+ int ret;
+
+ hisi_soc_cache_framework_data_init();
+
+ ret = misc_register(&soc_cache_miscdev);
+ if (ret) {
+ hisi_soc_cache_inst_uninit();
+ return ret;
+ }
+
+ hisi_soc_cache_dbgfs_init();
+
+ return 0;
+}
+module_init(hisi_soc_cache_framework_init);
+
+static void __exit hisi_soc_cache_framework_exit(void)
+{
+ hisi_soc_cache_dbgfs_uninit();
+ misc_deregister(&soc_cache_miscdev);
+ hisi_soc_cache_inst_uninit();
+}
+module_exit(hisi_soc_cache_framework_exit);
+
+MODULE_DESCRIPTION("HiSilicon SoC Cache Framework Driver");
+MODULE_AUTHOR("Jie Wang <wangjie125@huawei.com>");
+MODULE_AUTHOR("Yushan Wang <wangyushan12@huawei.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/hisilicon/hisi_soc_cache_framework.h b/drivers/soc/hisilicon/hisi_soc_cache_framework.h
new file mode 100644
index 000000000000..ceea161746cf
--- /dev/null
+++ b/drivers/soc/hisilicon/hisi_soc_cache_framework.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Header file of framework for HiSilicon SoC cache.
+ *
+ * Copyright (c) 2024 HiSilicon Technologies Co., Ltd.
+ * Author: Jie Wang <wangjie125@huawei.com>
+ * Author: Yicong Yang <yangyicong@hisilicon.com>
+ * Author: Yushan Wang <wangyushan12@huawei.com>
+ */
+
+#ifndef HISI_CACHE_FRAMEWORK_H
+#define HISI_CACHE_FRAMEWORK_H
+
+#include <linux/bits.h>
+#include <linux/types.h>
+
+enum hisi_soc_comp_type {
+ HISI_SOC_L3C,
+ SOC_COMP_TYPE_MAX
+};
+
+struct hisi_soc_comp;
+
+/**
+ * struct hisi_soc_comp_ops - Callbacks for SoC cache drivers to handle
+ * operation requests.
+ * @lock_enable: lock certain region of L3 cache from being evicted.
+ * @poll_lock_done: check if the lock operation has succeeded.
+ * @unlock_enable: unlock the locked region of L3 cache back to normal.
+ * @poll_unlock_done: check if the unlock operation has succeeded.
+ *
+ * Operations are decoupled into two phases so that framework does not have
+ * to wait for one operation to finish before calling the next when multiple
+ * hardwares onboard.
+ *
+ * Implementers must implement the functions in pairs. Implementation should
+ * return -EBUSY when:
+ * - insufficient resources are available to perform the operation.
+ * - previously raised operation is not finished.
+ * - new operations (do_lock(), do_unlock() etc.) to the same address
+ * before corresponding done functions being called.
+ */
+struct hisi_soc_comp_ops {
+ int (*do_lock)(struct hisi_soc_comp *comp,
+ phys_addr_t addr, size_t size);
+ int (*poll_lock_done)(struct hisi_soc_comp *comp,
+ phys_addr_t addr, size_t size);
+ int (*do_unlock)(struct hisi_soc_comp *comp,
+ phys_addr_t addr);
+ int (*poll_unlock_done)(struct hisi_soc_comp *comp,
+ phys_addr_t addr);
+};
+
+/**
+ * struct hisi_soc_comp - Struct of HiSilicon SoC cache components.
+ * @ops: possible operations a component may perform.
+ * @affinity_mask: cpus that associate with this component.
+ * @comp_type: bitmap declaring the type of the component.
+ *
+ * A component may have multiple types (e.g. a piece of multi-function device).
+ * If so, set the bit of @comp_type according to its supporting type in struct
+ * hisi_soc_comp_type.
+ */
+struct hisi_soc_comp {
+ struct hisi_soc_comp_ops *ops;
+ cpumask_t affinity_mask;
+ /*
+ * Setting bit x to 1 means this instance supports feature of x-th
+ * entry in enum hisi_soc_comp_type.
+ */
+ unsigned long comp_type;
+};
+
+int hisi_soc_comp_inst_add(struct hisi_soc_comp *comp);
+int hisi_soc_comp_inst_del(struct hisi_soc_comp *comp);
+
+#endif
--
2.33.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/2] soc cache: L3 cache lockdown support for HiSilicon SoC
2025-01-07 13:29 [PATCH 0/2] soc cache: Add support for HiSilicon L3 cache Yushan Wang
2025-01-07 13:29 ` [PATCH 1/2] soc cache: Add framework driver for HiSilicon SoC cache Yushan Wang
@ 2025-01-07 13:29 ` Yushan Wang
2025-01-08 14:37 ` kernel test robot
2025-01-08 15:32 ` kernel test robot
1 sibling, 2 replies; 8+ messages in thread
From: Yushan Wang @ 2025-01-07 13:29 UTC (permalink / raw)
To: xuwei5, yangyicong, Jonathan.Cameron, wangjie125, linux-kernel
Cc: prime.zeng, fanghao11, wangyushan12, linuxarm
This driver implements the interface exposed by framework, passes cache
lock/unlock requests to hardware.
L3 cache and L3 cache PMU share the same memory resource, which makes
one fails to probe while another is on board. Since both devices
rely on distinct information exported by ACPI, make their probing
functions unrelated and workaround resource conflict check by replacing
devm_ioremap_resource() to devm_ioremap().
Signed-off-by: Yushan Wang <wangyushan12@huawei.com>
---
drivers/soc/hisilicon/Kconfig | 11 +
drivers/soc/hisilicon/Makefile | 1 +
drivers/soc/hisilicon/hisi_soc_l3c.c | 540 +++++++++++++++++++++++++++
3 files changed, 552 insertions(+)
create mode 100644 drivers/soc/hisilicon/hisi_soc_l3c.c
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
index ef826c895a9d..7b44a5699950 100644
--- a/drivers/soc/hisilicon/Kconfig
+++ b/drivers/soc/hisilicon/Kconfig
@@ -32,4 +32,15 @@ config HISI_SOC_CACHE
If either HiSilicon L3 cache driver or HiSilicon Hydra Home
Agent driver is needed, say yes.
+config HISI_SOC_L3C
+ tristate "HiSilicon L3 Cache device driver"
+ depends on ARM64 && ACPI || COMPILE_TEST
+ depends on HISI_SOC_CACHE
+ help
+ This driver provides the functions to lock L3 cache entries from
+ being evicted for better performance.
+
+ This driver can be built as a module. If so, the module will be
+ called hisi_soc_l3c.
+
endmenu
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
index dbc673b71d4d..139ba3608984 100644
--- a/drivers/soc/hisilicon/Makefile
+++ b/drivers/soc/hisilicon/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_KUNPENG_HCCS) += kunpeng_hccs.o
obj-$(CONFIG_HISI_SOC_CACHE) += hisi_soc_cache_framework.o
+obj-$(CONFIG_HISI_SOC_L3C) += hisi_soc_l3c.o
diff --git a/drivers/soc/hisilicon/hisi_soc_l3c.c b/drivers/soc/hisilicon/hisi_soc_l3c.c
new file mode 100644
index 000000000000..b49f13ea2b3e
--- /dev/null
+++ b/drivers/soc/hisilicon/hisi_soc_l3c.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for HiSilicon L3 cache.
+ *
+ * Copyright (c) 2024 HiSilicon Technologies Co., Ltd.
+ * Author: Yushan Wang <wangyushan12@huawei.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#include <linux/cpuhotplug.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+#include <linux/xarray.h>
+
+#include "hisi_soc_cache_framework.h"
+
+#define HISI_L3C_LOCK_CTRL 0x0530
+#define HISI_L3C_LOCK_AREA 0x0534
+#define HISI_L3C_LOCK_START_L 0x0538
+#define HISI_L3C_LOCK_START_H 0x053C
+
+#define HISI_L3C_LOCK_CTRL_POLL_GAP_US 10
+#define HISI_L3C_LOCK_CTRL_POLL_TIMEOUT_US 50000
+
+#define HISI_L3C_MAX_LOCKREGION_SIZE \
+ "hisilicon,l3c-max-single-lockregion-size"
+#define HISI_L3C_MAX_LOCKREGION_NUM \
+ "hisilicon,l3c-lockregion-num"
+
+/* L3C control register bit definition */
+#define HISI_L3C_LOCK_CTRL_LOCK_EN BIT(0)
+#define HISI_L3C_LOCK_CTRL_LOCK_DONE BIT(1)
+#define HISI_L3C_LOCK_CTRL_UNLOCK_EN BIT(2)
+#define HISI_L3C_LOCK_CTRL_UNLOCK_DONE BIT(3)
+
+#define l3c_lock_reg_offset(reg, set) ((reg) + 16 * (set))
+
+#define l3c_lock_ctrl_mask(lock_ctrl, mask) ((lock_ctrl) & (mask))
+
+#define to_hisi_l3c(p) container_of((p), struct hisi_soc_l3c, comp)
+
+static int hisi_l3c_cpuhp_state;
+
+struct hisi_soc_l3c {
+ struct hisi_soc_comp comp;
+ cpumask_t associated_cpus;
+
+ /* Stores the first address locked by each register sets. */
+ struct xarray lock_sets;
+ /* Stores if a set of lock control register has been used. */
+ u32 reg_used_map;
+ /* Locks reg_used_map and lock_sets to forbid overlapping access. */
+ spinlock_t reg_lock;
+
+ /* Maximum locked memory size for a single lock region. */
+ unsigned int max_single_lock_size;
+ /* Maximum number of locked memory size. */
+ int max_lock_num;
+
+ struct hlist_node node;
+ void __iomem *base;
+
+ /* ID of Super CPU cluster on where the L3 cache locates. */
+ int sccl_id;
+ /* ID of CPU cluster where L3 cache is located. */
+ int ccl_id;
+};
+
+/**
+ * hisi_soc_l3c_alloc_lock_reg_set - Allocate an available control register set
+ * of L3 cache for lock & unlock operations.
+ * @soc_l3c: The L3C instance on which the register set will be allocated.
+ * @addr: The address to be locked.
+ *
+ * @return:
+ * - -EBUSY: If there is no available register sets.
+ * - 0: If allocation succeeds.
+ *
+ * Maintains the resource of control registers of L3 cache. On allocation,
+ * the index of a spare set of registers is returned, then the address is
+ * stored inside for future match of unlock operation.
+ */
+static int hisi_soc_l3c_alloc_lock_reg_set(struct hisi_soc_l3c *soc_l3c,
+ phys_addr_t addr)
+{
+ unsigned long idx;
+ void *entry;
+
+ for (idx = 0; idx < soc_l3c->max_lock_num; ++idx) {
+ entry = xa_load(&soc_l3c->lock_sets, idx);
+ if (!entry)
+ break;
+ }
+
+ if (idx >= soc_l3c->max_lock_num)
+ return -EBUSY;
+
+ xa_store(&soc_l3c->lock_sets, idx, xa_mk_value(addr), GFP_KERNEL);
+ return idx;
+}
+
+/**
+ * hisi_soc_l3c_get_locked_reg_set - Get the index of an allocated register set
+ * by locked address.
+ * @soc_l3c: The L3C instance on which the register set is allocated.
+ * @addr: The locked address.
+ *
+ * @return:
+ * - >= 0: index of register set which controls locked memory region of @addr.
+ * - -EINVAL: If @addr is not locked in this cache.
+ */
+static int hisi_soc_l3c_get_locked_reg_set(struct hisi_soc_l3c *soc_l3c,
+ phys_addr_t addr)
+{
+ void *entry;
+ unsigned long idx;
+
+ xa_for_each_range(&soc_l3c->lock_sets, idx, entry, 0,
+ soc_l3c->max_lock_num) {
+ if (xa_to_value(entry) == addr)
+ return idx;
+ }
+ return -EINVAL;
+}
+
+/**
+ * hisi_soc_l3c_free_lock_reg_set - Free an allocated register set by locked
+ * address.
+ *
+ * @soc_l3c: The L3C instance on which the register set is allocated.
+ * @addr: The locked address.
+ */
+static void hisi_soc_l3c_free_lock_reg_set(struct hisi_soc_l3c *soc_l3c,
+ int regset)
+{
+ if (regset < 0)
+ return;
+
+ xa_erase(&soc_l3c->lock_sets, regset);
+}
+
+static int hisi_l3c_lock_ctrl_wait_finished(struct hisi_soc_l3c *soc_l3c,
+ int regset, u32 mask)
+{
+ u32 reg_used_map = soc_l3c->reg_used_map;
+ void *base = soc_l3c->base;
+ u32 val;
+
+ /*
+ * Each HiSilicon L3 cache instance will have lock/unlock done bit set
+ * to 0 when first put to use even if the device is available.
+ * A reg_used_map is proposed to record if an instance has been called
+ * to lock down, then we can determine if it is available by
+ * reading lock/unlock done bit.
+ */
+ if (!(reg_used_map & BIT(regset))) {
+ reg_used_map |= BIT(regset);
+ return 1;
+ }
+
+ return !readl_poll_timeout_atomic(
+ base + l3c_lock_reg_offset(HISI_L3C_LOCK_CTRL, regset),
+ val, l3c_lock_ctrl_mask(val, mask),
+ HISI_L3C_LOCK_CTRL_POLL_GAP_US,
+ HISI_L3C_LOCK_CTRL_POLL_TIMEOUT_US);
+}
+
+static int hisi_soc_l3c_do_lock(struct hisi_soc_comp *l3c_comp,
+ phys_addr_t addr, size_t size)
+{
+ struct hisi_soc_l3c *soc_l3c = to_hisi_l3c(l3c_comp);
+ void *base = soc_l3c->base;
+ int regset;
+ u32 ctrl;
+
+ if (size > soc_l3c->max_single_lock_size)
+ return -EINVAL;
+
+ guard(spinlock)(&soc_l3c->reg_lock);
+
+ regset = hisi_soc_l3c_alloc_lock_reg_set(soc_l3c, addr);
+ if (regset < 0)
+ return -EBUSY;
+
+ if (!hisi_l3c_lock_ctrl_wait_finished(soc_l3c, regset,
+ HISI_L3C_LOCK_CTRL_LOCK_DONE)) {
+ hisi_soc_l3c_free_lock_reg_set(soc_l3c, regset);
+ return -EBUSY;
+ }
+
+ writel(lower_32_bits(addr),
+ base + l3c_lock_reg_offset(HISI_L3C_LOCK_START_L, regset));
+ writel(upper_32_bits(addr),
+ base + l3c_lock_reg_offset(HISI_L3C_LOCK_START_H, regset));
+ writel(size, base + l3c_lock_reg_offset(HISI_L3C_LOCK_AREA, regset));
+
+ ctrl = readl(base + l3c_lock_reg_offset(HISI_L3C_LOCK_CTRL, regset));
+ ctrl = (ctrl | HISI_L3C_LOCK_CTRL_LOCK_EN) &
+ ~HISI_L3C_LOCK_CTRL_UNLOCK_EN;
+ writel(ctrl, base + l3c_lock_reg_offset(HISI_L3C_LOCK_CTRL, regset));
+
+ return 0;
+}
+
+static int hisi_soc_l3c_poll_lock_done(struct hisi_soc_comp *l3c_comp,
+ phys_addr_t addr, size_t size)
+{
+ struct hisi_soc_l3c *soc_l3c = to_hisi_l3c(l3c_comp);
+ int regset;
+
+ guard(spinlock)(&soc_l3c->reg_lock);
+
+ regset = hisi_soc_l3c_get_locked_reg_set(soc_l3c, addr);
+ if (regset < 0)
+ return -EINVAL;
+
+ if (!hisi_l3c_lock_ctrl_wait_finished(soc_l3c, regset,
+ HISI_L3C_LOCK_CTRL_LOCK_DONE))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int hisi_soc_l3c_do_unlock(struct hisi_soc_comp *l3c_comp,
+ phys_addr_t addr)
+{
+ struct hisi_soc_l3c *soc_l3c = to_hisi_l3c(l3c_comp);
+ void *base = soc_l3c->base;
+ int regset;
+ u32 ctrl;
+
+ guard(spinlock)(&soc_l3c->reg_lock);
+
+ regset = hisi_soc_l3c_get_locked_reg_set(soc_l3c, addr);
+ if (regset < 0)
+ return -EINVAL;
+
+ if (!hisi_l3c_lock_ctrl_wait_finished(soc_l3c, regset,
+ HISI_L3C_LOCK_CTRL_UNLOCK_DONE))
+ return -EBUSY;
+
+ ctrl = readl(base + l3c_lock_reg_offset(HISI_L3C_LOCK_CTRL, regset));
+ ctrl = (ctrl | HISI_L3C_LOCK_CTRL_UNLOCK_EN) &
+ ~HISI_L3C_LOCK_CTRL_LOCK_EN;
+ writel(ctrl, base + l3c_lock_reg_offset(HISI_L3C_LOCK_CTRL, regset));
+
+ return 0;
+}
+
+static int hisi_soc_l3c_poll_unlock_done(struct hisi_soc_comp *l3c_comp,
+ phys_addr_t addr)
+{
+ struct hisi_soc_l3c *soc_l3c = to_hisi_l3c(l3c_comp);
+ int regset;
+
+ guard(spinlock)(&soc_l3c->reg_lock);
+
+ regset = hisi_soc_l3c_get_locked_reg_set(soc_l3c, addr);
+ if (regset < 0)
+ return -EINVAL;
+
+ if (!hisi_l3c_lock_ctrl_wait_finished(soc_l3c, regset,
+ HISI_L3C_LOCK_CTRL_UNLOCK_DONE))
+ return -ETIMEDOUT;
+
+ hisi_soc_l3c_free_lock_reg_set(soc_l3c, regset);
+
+ return 0;
+}
+
+/**
+ * hisi_soc_l3c_remove_locks - Remove all cache locks when the driver exits.
+ *
+ * @soc_l3c: The L3C instance on which the cache locks should be removed.
+ */
+static void hisi_soc_l3c_remove_locks(struct hisi_soc_l3c *soc_l3c)
+{
+
+ void *base = soc_l3c->base;
+ unsigned long regset;
+ int timeout;
+ void *entry;
+ u32 ctrl;
+
+ guard(spinlock)(&soc_l3c->reg_lock);
+
+ xa_for_each(&soc_l3c->lock_sets, regset, entry) {
+ timeout = hisi_l3c_lock_ctrl_wait_finished(soc_l3c, regset,
+ HISI_L3C_LOCK_CTRL_UNLOCK_DONE);
+
+ ctrl = readl(base + l3c_lock_reg_offset(HISI_L3C_LOCK_CTRL,
+ regset));
+ ctrl = (ctrl | HISI_L3C_LOCK_CTRL_UNLOCK_EN) &
+ ~HISI_L3C_LOCK_CTRL_LOCK_EN;
+ writel(ctrl, base + l3c_lock_reg_offset(HISI_L3C_LOCK_CTRL,
+ regset));
+
+ timeout = hisi_l3c_lock_ctrl_wait_finished(soc_l3c, regset,
+ HISI_L3C_LOCK_CTRL_UNLOCK_DONE);
+
+ /*
+ * If cache lock remove fails, inform user since the removal of
+ * driver cannot fail.
+ */
+ if (timeout)
+ pr_err("failed to remove %ldth cache lock.\n", regset);
+ }
+}
+
+static int hisi_soc_l3c_init_lock_capacity(struct hisi_soc_l3c *soc_l3c,
+ struct device *dev)
+{
+ if (device_property_read_u32(dev, HISI_L3C_MAX_LOCKREGION_SIZE,
+ &soc_l3c->max_single_lock_size)
+ || soc_l3c->max_single_lock_size <= 0)
+ return -EINVAL;
+
+ if (device_property_read_u32(dev, HISI_L3C_MAX_LOCKREGION_NUM,
+ &soc_l3c->max_lock_num)
+ || soc_l3c->max_lock_num <= 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int hisi_soc_l3c_init_topology(struct hisi_soc_l3c *soc_l3c,
+ struct device *dev)
+{
+ soc_l3c->sccl_id = -1;
+ soc_l3c->ccl_id = -1;
+
+ if (device_property_read_u32(dev, "hisilicon,scl-id", &soc_l3c->sccl_id)
+ || soc_l3c->sccl_id < 0)
+ return -EINVAL;
+
+ if (device_property_read_u32(dev, "hisilicon,ccl-id", &soc_l3c->ccl_id)
+ || soc_l3c->ccl_id < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void hisi_init_associated_cpus(struct hisi_soc_l3c *soc_l3c)
+{
+ if (!cpumask_empty(&soc_l3c->associated_cpus))
+ return;
+ cpumask_clear(&soc_l3c->associated_cpus);
+ cpumask_copy(&soc_l3c->comp.affinity_mask, &soc_l3c->associated_cpus);
+}
+
+static struct hisi_soc_comp_ops hisi_soc_l3c_comp_ops = {
+ .do_lock = hisi_soc_l3c_do_lock,
+ .poll_lock_done = hisi_soc_l3c_poll_lock_done,
+ .do_unlock = hisi_soc_l3c_do_unlock,
+ .poll_unlock_done = hisi_soc_l3c_poll_unlock_done,
+};
+
+static struct hisi_soc_comp hisi_soc_l3c_comp = {
+ .comp_type = BIT(HISI_SOC_L3C),
+ .ops = &hisi_soc_l3c_comp_ops,
+};
+
+static int hisi_soc_l3c_probe(struct platform_device *pdev)
+{
+ struct hisi_soc_l3c *soc_l3c;
+ struct resource *mem;
+ int ret = 0;
+
+ soc_l3c = devm_kzalloc(&pdev->dev, sizeof(*soc_l3c), GFP_KERNEL);
+ if (!soc_l3c)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, soc_l3c);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -ENODEV;
+
+ /*
+ * L3C cache driver share the same register region with L3C uncore PMU
+ * driver in hardware's perspective, none of them should reserve the
+ * resource to itself only. Here exclusive access verification is
+ * avoided by calling devm_ioremap instead of devm_ioremap_resource to
+ * allow both drivers to exist at the same time.
+ */
+ soc_l3c->base = devm_ioremap(&pdev->dev, mem->start,
+ resource_size(mem));
+ if (IS_ERR_OR_NULL(soc_l3c->base))
+ return PTR_ERR(soc_l3c->base);
+
+ soc_l3c->comp = hisi_soc_l3c_comp;
+ spin_lock_init(&soc_l3c->reg_lock);
+ xa_init(&soc_l3c->lock_sets);
+
+ ret = hisi_soc_l3c_init_lock_capacity(soc_l3c, &pdev->dev);
+ if (ret)
+ goto err_xa;
+
+ hisi_init_associated_cpus(soc_l3c);
+
+ ret = hisi_soc_l3c_init_topology(soc_l3c, &pdev->dev);
+ if (ret)
+ goto err_xa;
+
+ ret = cpuhp_state_add_instance(hisi_l3c_cpuhp_state, &soc_l3c->node);
+ if (ret)
+ goto err_xa;
+
+ ret = hisi_soc_comp_inst_add(&soc_l3c->comp);
+ if (ret)
+ goto err_hotplug;
+
+ return ret;
+
+err_hotplug:
+ cpuhp_state_remove_instance_nocalls(hisi_l3c_cpuhp_state,
+ &soc_l3c->node);
+
+err_xa:
+ xa_destroy(&soc_l3c->lock_sets);
+ return ret;
+}
+
+static void hisi_soc_l3c_remove(struct platform_device *pdev)
+{
+ struct hisi_soc_l3c *soc_l3c = platform_get_drvdata(pdev);
+ unsigned long idx;
+ void *entry;
+
+ hisi_soc_l3c_remove_locks(soc_l3c);
+
+ hisi_soc_comp_inst_del(&soc_l3c->comp);
+
+ cpuhp_state_remove_instance_nocalls(hisi_l3c_cpuhp_state,
+ &soc_l3c->node);
+
+ xa_for_each(&soc_l3c->lock_sets, idx, entry)
+ xa_erase(&soc_l3c->lock_sets, idx);
+
+ xa_destroy(&soc_l3c->lock_sets);
+}
+
+static void hisi_read_sccl_and_ccl_id(int *scclp, int *cclp)
+{
+ u64 mpidr = read_cpuid_mpidr();
+ int aff3 = MPIDR_AFFINITY_LEVEL(mpidr, 3);
+ int aff2 = MPIDR_AFFINITY_LEVEL(mpidr, 2);
+ int aff1 = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+ int sccl, ccl;
+
+ if (mpidr & MPIDR_MT_BITMASK) {
+ sccl = aff3;
+ ccl = aff2;
+ } else {
+ sccl = aff2;
+ ccl = aff1;
+ }
+
+ *scclp = sccl;
+ *cclp = ccl;
+}
+
+static bool hisi_soc_l3c_is_associated(struct hisi_soc_l3c *soc_l3c)
+{
+ int sccl_id, ccl_id;
+
+ hisi_read_sccl_and_ccl_id(&sccl_id, &ccl_id);
+ return sccl_id == soc_l3c->sccl_id && ccl_id == soc_l3c->ccl_id;
+}
+
+static int hisi_soc_l3c_online_cpu(unsigned int cpu, struct hlist_node *node)
+{
+ struct hisi_soc_l3c *soc_l3c =
+ hlist_entry_safe(node, struct hisi_soc_l3c, node);
+
+ if (!cpumask_test_cpu(cpu, &soc_l3c->associated_cpus)) {
+ if (!(hisi_soc_l3c_is_associated(soc_l3c)))
+ return 0;
+
+ cpumask_set_cpu(cpu, &soc_l3c->associated_cpus);
+ cpumask_copy(&soc_l3c->comp.affinity_mask,
+ &soc_l3c->associated_cpus);
+ }
+ return 0;
+}
+
+static const struct acpi_device_id hisi_l3c_acpi_match[] = {
+ { "HISI0501", },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, hisi_l3c_acpi_match);
+
+static struct platform_driver hisi_soc_l3c_driver = {
+ .driver = {
+ .name = "hisi_soc_l3c",
+ .acpi_match_table = hisi_l3c_acpi_match,
+ },
+ .probe = hisi_soc_l3c_probe,
+ .remove = hisi_soc_l3c_remove,
+};
+
+static int __init hisi_soc_l3c_init(void)
+{
+ int ret;
+
+ ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "hisi_soc_l3c",
+ hisi_soc_l3c_online_cpu, NULL);
+ if (ret < 0)
+ return ret;
+ hisi_l3c_cpuhp_state = ret;
+
+ ret = platform_driver_register(&hisi_soc_l3c_driver);
+ if (ret)
+ cpuhp_remove_multi_state(CPUHP_AP_ONLINE_DYN);
+
+ return ret;
+}
+module_init(hisi_soc_l3c_init);
+
+static void __exit hisi_soc_l3c_exit(void)
+{
+ platform_driver_unregister(&hisi_soc_l3c_driver);
+ cpuhp_remove_multi_state(CPUHP_AP_ONLINE_DYN);
+}
+module_exit(hisi_soc_l3c_exit);
+
+MODULE_DESCRIPTION("Driver supporting cache lockdown for Hisilicon L3 cache");
+MODULE_AUTHOR("Yushan Wang <wangyushan12@huawei.com>");
+MODULE_LICENSE("GPL");
--
2.33.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] soc cache: Add framework driver for HiSilicon SoC cache
2025-01-07 13:29 ` [PATCH 1/2] soc cache: Add framework driver for HiSilicon SoC cache Yushan Wang
@ 2025-01-07 19:05 ` Christophe JAILLET
2025-01-08 9:59 ` wangyushan
2025-01-08 13:54 ` kernel test robot
1 sibling, 1 reply; 8+ messages in thread
From: Christophe JAILLET @ 2025-01-07 19:05 UTC (permalink / raw)
To: Yushan Wang, xuwei5, yangyicong, Jonathan.Cameron, wangjie125,
linux-kernel
Cc: prime.zeng, fanghao11, linuxarm
Le 07/01/2025 à 14:29, Yushan Wang a écrit :
> From: Jie Wang <wangjie125@huawei.com>
>
> HiSilicon SoC cache is comprised of multiple hardware devices, a driver
> in this patch is used to provide common utilities for other drivers to
> avoid redundancy.
...
> +static int hisi_soc_cache_lock(int cpu, phys_addr_t addr, size_t size)
> +{
> + struct hisi_soc_comp_inst *inst;
> + struct list_head *head;
> + int ret = -ENOMEM;
> +
> + guard(spinlock)(&soc_cache_devs[HISI_SOC_L3C].lock);
> +
> + /* Iterate L3C instances to perform operation, break loop once found. */
> + head = &soc_cache_devs[HISI_SOC_L3C].node;
> + list_for_each_entry(inst, head, node) {
> + if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
> + continue;
> + ret = inst->comp->ops->do_lock(inst->comp, addr, size);
> + if (ret)
> + return ret;
> + break;
> + }
> +
> + list_for_each_entry(inst, head, node) {
Do we need to iterate another time.
Isn't "inst" already correct?
If so, I guess that:
ret = inst->comp->ops->poll_lock_done(inst->comp, addr, size)
if (ret)
return ret;
could be moved at the end the previous loop to both simplify the code,
and save a few cycles.
> + if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
> + continue;
> + ret = inst->comp->ops->poll_lock_done(inst->comp, addr, size);
> + if (ret)
> + return ret;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int hisi_soc_cache_unlock(int cpu, phys_addr_t addr)
> +{
> + struct hisi_soc_comp_inst *inst;
> + struct list_head *head;
> + int ret = 0;
> +
> + guard(spinlock)(&soc_cache_devs[HISI_SOC_L3C].lock);
> +
> + /* Iterate L3C instances to perform operation, break loop once found. */
> + head = &soc_cache_devs[HISI_SOC_L3C].node;
> + list_for_each_entry(inst, head, node) {
> + if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
> + continue;
> + ret = inst->comp->ops->do_unlock(inst->comp, addr);
> + if (ret)
> + return ret;
> + break;
> + }
> +
> + list_for_each_entry(inst, head, node) {
Same as above.
> + if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
> + continue;
> + ret = inst->comp->ops->poll_unlock_done(inst->comp, addr);
> + if (ret)
> + return ret;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int hisi_soc_cache_inst_check(const struct hisi_soc_comp *comp,
> + enum hisi_soc_comp_type comp_type)
> +{
> + struct hisi_soc_comp_ops *ops = comp->ops;
> +
> + /* Different types of component could have different ops. */
> + switch (comp_type) {
> + case HISI_SOC_L3C:
> + if (!ops->do_lock || !ops->poll_lock_done
> + || !ops->do_unlock || !ops->poll_unlock_done)
I think that || should be at the end of the previous line.
If I remember correctly checkpatch (maybe with --strict) complains about it.
> + return -EINVAL;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
...
CJ
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] soc cache: Add framework driver for HiSilicon SoC cache
2025-01-07 19:05 ` Christophe JAILLET
@ 2025-01-08 9:59 ` wangyushan
0 siblings, 0 replies; 8+ messages in thread
From: wangyushan @ 2025-01-08 9:59 UTC (permalink / raw)
To: Christophe JAILLET, xuwei5, yangyicong, Jonathan.Cameron,
wangjie125, linux-kernel
Cc: prime.zeng, fanghao11, linuxarm
On 2025/1/8 3:05, Christophe JAILLET wrote:
> Le 07/01/2025 à 14:29, Yushan Wang a écrit :
>> From: Jie Wang <wangjie125@huawei.com>
>>
>> HiSilicon SoC cache is comprised of multiple hardware devices, a driver
>> in this patch is used to provide common utilities for other drivers to
>> avoid redundancy.
>
> ...
>
>> +static int hisi_soc_cache_lock(int cpu, phys_addr_t addr, size_t size)
>> +{
>> + struct hisi_soc_comp_inst *inst;
>> + struct list_head *head;
>> + int ret = -ENOMEM;
>> +
>> + guard(spinlock)(&soc_cache_devs[HISI_SOC_L3C].lock);
>> +
>> + /* Iterate L3C instances to perform operation, break loop once
>> found. */
>> + head = &soc_cache_devs[HISI_SOC_L3C].node;
>> + list_for_each_entry(inst, head, node) {
>> + if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
>> + continue;
>> + ret = inst->comp->ops->do_lock(inst->comp, addr, size);
>> + if (ret)
>> + return ret;
>> + break;
>> + }
>> +
>> + list_for_each_entry(inst, head, node) {
>
> Do we need to iterate another time.
> Isn't "inst" already correct?
>
> If so, I guess that:
> ret = inst->comp->ops->poll_lock_done(inst->comp, addr, size)
> if (ret)
> return ret;
>
> could be moved at the end the previous loop to both simplify the code,
> and save a few cycles.
Yes, will fix that in the next version.
>
>> + if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
>> + continue;
>> + ret = inst->comp->ops->poll_lock_done(inst->comp, addr, size);
>> + if (ret)
>> + return ret;
>> + break;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static int hisi_soc_cache_unlock(int cpu, phys_addr_t addr)
>> +{
>> + struct hisi_soc_comp_inst *inst;
>> + struct list_head *head;
>> + int ret = 0;
>> +
>> + guard(spinlock)(&soc_cache_devs[HISI_SOC_L3C].lock);
>> +
>> + /* Iterate L3C instances to perform operation, break loop once
>> found. */
>> + head = &soc_cache_devs[HISI_SOC_L3C].node;
>> + list_for_each_entry(inst, head, node) {
>> + if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
>> + continue;
>> + ret = inst->comp->ops->do_unlock(inst->comp, addr);
>> + if (ret)
>> + return ret;
>> + break;
>> + }
>> +
>> + list_for_each_entry(inst, head, node) {
>
> Same as above.
Will fix here as well.
>
>> + if (!cpumask_test_cpu(cpu, &inst->comp->affinity_mask))
>> + continue;
>> + ret = inst->comp->ops->poll_unlock_done(inst->comp, addr);
>> + if (ret)
>> + return ret;
>> + break;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static int hisi_soc_cache_inst_check(const struct hisi_soc_comp *comp,
>> + enum hisi_soc_comp_type comp_type)
>> +{
>> + struct hisi_soc_comp_ops *ops = comp->ops;
>> +
>> + /* Different types of component could have different ops. */
>> + switch (comp_type) {
>> + case HISI_SOC_L3C:
>> + if (!ops->do_lock || !ops->poll_lock_done
>> + || !ops->do_unlock || !ops->poll_unlock_done)
>
> I think that || should be at the end of the previous line.
> If I remember correctly checkpatch (maybe with --strict) complains
> about it.
Yes, run checkpatch with --strict will generate some check advises.
Will fix this in the next version.
Thanks!
Regards,
Yushan
>
>> + return -EINVAL;
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>
> ...
>
> CJ
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] soc cache: Add framework driver for HiSilicon SoC cache
2025-01-07 13:29 ` [PATCH 1/2] soc cache: Add framework driver for HiSilicon SoC cache Yushan Wang
2025-01-07 19:05 ` Christophe JAILLET
@ 2025-01-08 13:54 ` kernel test robot
1 sibling, 0 replies; 8+ messages in thread
From: kernel test robot @ 2025-01-08 13:54 UTC (permalink / raw)
To: Yushan Wang, xuwei5, yangyicong, Jonathan.Cameron, wangjie125,
linux-kernel
Cc: oe-kbuild-all, prime.zeng, fanghao11, wangyushan12, linuxarm
Hi Yushan,
kernel test robot noticed the following build warnings:
[auto build test WARNING on linus/master]
[also build test WARNING on v6.13-rc6 next-20250107]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yushan-Wang/soc-cache-Add-framework-driver-for-HiSilicon-SoC-cache/20250107-213022
base: linus/master
patch link: https://lore.kernel.org/r/20250107132907.3521574-2-wangyushan12%40huawei.com
patch subject: [PATCH 1/2] soc cache: Add framework driver for HiSilicon SoC cache
config: arm-allmodconfig (https://download.01.org/0day-ci/archive/20250108/202501082116.RY64zFEz-lkp@intel.com/config)
compiler: arm-linux-gnueabi-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250108/202501082116.RY64zFEz-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202501082116.RY64zFEz-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/soc/hisilicon/hisi_soc_cache_framework.c: In function 'hisi_soc_cache_vm_close':
>> drivers/soc/hisilicon/hisi_soc_cache_framework.c:331:13: warning: variable 'ret' set but not used [-Wunused-but-set-variable]
331 | int ret;
| ^~~
vim +/ret +331 drivers/soc/hisilicon/hisi_soc_cache_framework.c
327
328 static void hisi_soc_cache_vm_close(struct vm_area_struct *vma)
329 {
330 struct hisi_soc_cache_lock_region *clr = vma->vm_private_data;
> 331 int ret;
332
333 /*
334 * Only perform cache unlock when the vma passed in is created
335 * in hisi_soc_cache_mmap.
336 */
337 if (clr->vm_start != vma->vm_start || clr->vm_end != vma->vm_end)
338 return;
339
340 ret = hisi_soc_cache_unlock(clr->cpu, clr->addr);
341
342 hisi_soc_cache_aligned_free(clr);
343 kfree(clr);
344 vma->vm_private_data = NULL;
345 }
346
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] soc cache: L3 cache lockdown support for HiSilicon SoC
2025-01-07 13:29 ` [PATCH 2/2] soc cache: L3 cache lockdown support for HiSilicon SoC Yushan Wang
@ 2025-01-08 14:37 ` kernel test robot
2025-01-08 15:32 ` kernel test robot
1 sibling, 0 replies; 8+ messages in thread
From: kernel test robot @ 2025-01-08 14:37 UTC (permalink / raw)
To: Yushan Wang, xuwei5, yangyicong, Jonathan.Cameron, wangjie125,
linux-kernel
Cc: llvm, oe-kbuild-all, prime.zeng, fanghao11, wangyushan12,
linuxarm
Hi Yushan,
kernel test robot noticed the following build warnings:
[auto build test WARNING on linus/master]
[also build test WARNING on v6.13-rc6 next-20250108]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yushan-Wang/soc-cache-Add-framework-driver-for-HiSilicon-SoC-cache/20250107-213022
base: linus/master
patch link: https://lore.kernel.org/r/20250107132907.3521574-3-wangyushan12%40huawei.com
patch subject: [PATCH 2/2] soc cache: L3 cache lockdown support for HiSilicon SoC
config: arm64-allmodconfig (https://download.01.org/0day-ci/archive/20250108/202501082259.J9fBJZTQ-lkp@intel.com/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250108/202501082259.J9fBJZTQ-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202501082259.J9fBJZTQ-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/soc/hisilicon/hisi_soc_l3c.c:146: warning: Function parameter or struct member 'regset' not described in 'hisi_soc_l3c_free_lock_reg_set'
>> drivers/soc/hisilicon/hisi_soc_l3c.c:146: warning: Excess function parameter 'addr' description in 'hisi_soc_l3c_free_lock_reg_set'
vim +146 drivers/soc/hisilicon/hisi_soc_l3c.c
136
137 /**
138 * hisi_soc_l3c_free_lock_reg_set - Free an allocated register set by locked
139 * address.
140 *
141 * @soc_l3c: The L3C instance on which the register set is allocated.
142 * @addr: The locked address.
143 */
144 static void hisi_soc_l3c_free_lock_reg_set(struct hisi_soc_l3c *soc_l3c,
145 int regset)
> 146 {
147 if (regset < 0)
148 return;
149
150 xa_erase(&soc_l3c->lock_sets, regset);
151 }
152
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] soc cache: L3 cache lockdown support for HiSilicon SoC
2025-01-07 13:29 ` [PATCH 2/2] soc cache: L3 cache lockdown support for HiSilicon SoC Yushan Wang
2025-01-08 14:37 ` kernel test robot
@ 2025-01-08 15:32 ` kernel test robot
1 sibling, 0 replies; 8+ messages in thread
From: kernel test robot @ 2025-01-08 15:32 UTC (permalink / raw)
To: Yushan Wang, xuwei5, yangyicong, Jonathan.Cameron, wangjie125,
linux-kernel
Cc: oe-kbuild-all, prime.zeng, fanghao11, wangyushan12, linuxarm
Hi Yushan,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on v6.13-rc6 next-20250108]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yushan-Wang/soc-cache-Add-framework-driver-for-HiSilicon-SoC-cache/20250107-213022
base: linus/master
patch link: https://lore.kernel.org/r/20250107132907.3521574-3-wangyushan12%40huawei.com
patch subject: [PATCH 2/2] soc cache: L3 cache lockdown support for HiSilicon SoC
config: arm-allmodconfig (https://download.01.org/0day-ci/archive/20250108/202501082340.1dunPeza-lkp@intel.com/config)
compiler: arm-linux-gnueabi-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250108/202501082340.1dunPeza-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202501082340.1dunPeza-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/soc/hisilicon/hisi_soc_l3c.c: In function 'hisi_read_sccl_and_ccl_id':
>> drivers/soc/hisilicon/hisi_soc_l3c.c:456:21: error: implicit declaration of function 'read_cpuid_mpidr' [-Wimplicit-function-declaration]
456 | u64 mpidr = read_cpuid_mpidr();
| ^~~~~~~~~~~~~~~~
>> drivers/soc/hisilicon/hisi_soc_l3c.c:457:20: error: implicit declaration of function 'MPIDR_AFFINITY_LEVEL' [-Wimplicit-function-declaration]
457 | int aff3 = MPIDR_AFFINITY_LEVEL(mpidr, 3);
| ^~~~~~~~~~~~~~~~~~~~
>> drivers/soc/hisilicon/hisi_soc_l3c.c:462:21: error: 'MPIDR_MT_BITMASK' undeclared (first use in this function)
462 | if (mpidr & MPIDR_MT_BITMASK) {
| ^~~~~~~~~~~~~~~~
drivers/soc/hisilicon/hisi_soc_l3c.c:462:21: note: each undeclared identifier is reported only once for each function it appears in
vim +/read_cpuid_mpidr +456 drivers/soc/hisilicon/hisi_soc_l3c.c
453
454 static void hisi_read_sccl_and_ccl_id(int *scclp, int *cclp)
455 {
> 456 u64 mpidr = read_cpuid_mpidr();
> 457 int aff3 = MPIDR_AFFINITY_LEVEL(mpidr, 3);
458 int aff2 = MPIDR_AFFINITY_LEVEL(mpidr, 2);
459 int aff1 = MPIDR_AFFINITY_LEVEL(mpidr, 1);
460 int sccl, ccl;
461
> 462 if (mpidr & MPIDR_MT_BITMASK) {
463 sccl = aff3;
464 ccl = aff2;
465 } else {
466 sccl = aff2;
467 ccl = aff1;
468 }
469
470 *scclp = sccl;
471 *cclp = ccl;
472 }
473
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-01-08 15:33 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-07 13:29 [PATCH 0/2] soc cache: Add support for HiSilicon L3 cache Yushan Wang
2025-01-07 13:29 ` [PATCH 1/2] soc cache: Add framework driver for HiSilicon SoC cache Yushan Wang
2025-01-07 19:05 ` Christophe JAILLET
2025-01-08 9:59 ` wangyushan
2025-01-08 13:54 ` kernel test robot
2025-01-07 13:29 ` [PATCH 2/2] soc cache: L3 cache lockdown support for HiSilicon SoC Yushan Wang
2025-01-08 14:37 ` kernel test robot
2025-01-08 15:32 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox