[PATCH OLK-6.6 00/10] cache: Support cache maintenance for HiSilicon SoC Hydra Home Agent
From: Hongye Lin <linhongye@h-partners.com> driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10043 ---------------------------------------------------------------------- Jonathan Cameron (4): memregion: Drop unused IORES_DESC_* parameter from cpu_cache_invalidate_memregion() arm64: Select GENERIC_CPU_CACHE_MAINTENANCE MAINTAINERS: Add Jonathan Cameron to drivers/cache and add lib/cache_maint.c + header cache: Make top level Kconfig menu a boolean dependent on RISCV Yicong Yang (2): memregion: Support fine grained invalidate by cpu_cache_invalidate_memregion() lib: Support ARCH_HAS_CPU_CACHE_INVALIDATE_MEMREGION Yushan Wang (4): mm: reclaim_notify: move struct reclaim_notify_data out of CONFIG_RECLAIM_NOTIFY soc cache: remove old cache maintain cache: Support cache maintenance for HiSilicon SoC Hydra Home Agent defconfig: Add cache maintain related config MAINTAINERS | 4 +- arch/arm64/Kconfig | 2 + arch/arm64/configs/openeuler_defconfig | 9 +- arch/x86/mm/pat/set_memory.c | 2 +- drivers/cache/Kconfig | 28 +++- drivers/cache/Makefile | 2 + .../{soc/hisilicon => cache}/hisi_soc_hha.c | 152 +++++++++--------- drivers/cxl/core/region.c | 5 +- drivers/nvdimm/region.c | 2 +- drivers/nvdimm/region_devs.c | 2 +- drivers/soc/hisilicon/Kconfig | 12 -- drivers/soc/hisilicon/Makefile | 1 - .../soc/hisilicon/hisi_soc_cache_framework.c | 112 ------------- .../soc/hisilicon/hisi_soc_cache_framework.h | 10 -- include/linux/cache_coherency.h | 61 +++++++ include/linux/memregion.h | 16 +- include/linux/mm.h | 3 +- .../uapi/misc/hisi_soc_cache/hisi_soc_cache.h | 37 ----- lib/Kconfig | 3 + lib/Makefile | 2 + lib/cache_maint.c | 138 ++++++++++++++++ 21 files changed, 339 insertions(+), 264 deletions(-) rename drivers/{soc/hisilicon => cache}/hisi_soc_hha.c (50%) create mode 100644 include/linux/cache_coherency.h delete mode 100644 include/uapi/misc/hisi_soc_cache/hisi_soc_cache.h create mode 100644 lib/cache_maint.c -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10043 ---------------------------------------------------------------------- struct reclaim_notify_data is currently defined inside the does not depend on any CONFIG_RECLAIM_NOTIFY-gated type — every field (int, bool, unsigned long, the always-defined MAX_NUMNODES array, and enum reclaim_reason, which already lives outside the ifdef) is unconditionally available. However, the struct is referenced by code that is compiled regardless of CONFIG_RECLAIM_NOTIFY: obmm_lowmem.c (built under CONFIG_OBMM) and sentry_reporter.c (built under CONFIG_UB_SENTRY) both dereference it without their own ifdef guards. On the bigdipperv5r9_prod_64k build the page_64k fragment disables ARM64_4K_PAGES, which unmet's NUMA_REMOTE's "depends on ARM64_4K_PAGES". NUMA_REMOTE is forced off, and because RECLAIM_NOTIFY depends on NUMA_REMOTE, RECLAIM_NOTIFY is also forced off — even though both the base defconfig and the bigdipper_v5r9 fragment request it. With the symbol off, struct reclaim_notify_data is no longer defined, yet obmm_lowmem.o and sentry_reporter.o are still compiled, breaking the 64K build. Hoist the struct definition out of the #ifdef so the type is always available to its unconditional users. The notifier chain API (register/unregister/do_reclaim_notifier) remains gated, so when CONFIG_RECLAIM_NOTIFY=n those entry points still collapse to no-ops as before; only the passive payload type becomes unconditionally visible. Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- include/linux/mm.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index dfe362127354..62da3b473511 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -4415,8 +4415,6 @@ enum reclaim_reason { RR_TYPES }; -#ifdef CONFIG_RECLAIM_NOTIFY - struct reclaim_notify_data { int nr_nid; /* Number of nodes in nid[] */ int nid[MAX_NUMNODES]; /* Nodes who getting trouble in reclaiming */ @@ -4442,6 +4440,7 @@ struct reclaim_notify_data { unsigned long nr_freed; }; +#ifdef CONFIG_RECLAIM_NOTIFY int register_reclaim_notifier(struct notifier_block *nb); int unregister_reclaim_notifier(struct notifier_block *nb); unsigned long do_reclaim_notify(enum reclaim_reason reason, -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10043 ---------------------------------------------------------------------- Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/soc/hisilicon/Kconfig | 12 -- drivers/soc/hisilicon/Makefile | 1 - .../soc/hisilicon/hisi_soc_cache_framework.c | 112 ---------- .../soc/hisilicon/hisi_soc_cache_framework.h | 10 - drivers/soc/hisilicon/hisi_soc_hha.c | 194 ------------------ .../uapi/misc/hisi_soc_cache/hisi_soc_cache.h | 37 ---- 6 files changed, 366 deletions(-) delete mode 100644 drivers/soc/hisilicon/hisi_soc_hha.c delete mode 100644 include/uapi/misc/hisi_soc_cache/hisi_soc_cache.h diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig index 4d501a4c4c5b..b2b2403d7ac2 100644 --- a/drivers/soc/hisilicon/Kconfig +++ b/drivers/soc/hisilicon/Kconfig @@ -78,16 +78,4 @@ config HISI_SOC_L3C This driver can be built as a module. If so, the module will be called hisi_soc_l3c. - -config HISI_SOC_HHA - tristate "HiSilicon Hydra Home Agent (HHA) device driver" - depends on ARM64 && ACPI || COMPILE_TEST - depends on HISI_SOC_CACHE - help - The Hydra Home Agent (HHA) is responsible of cache coherency - on SoC. This drivers provides cache maintenance functions of HHA. - - This driver can be built as a module. If so, the module will be - called hisi_soc_hha. - endmenu diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile index 21f4bdb277ff..d53d5c5f2cb2 100644 --- a/drivers/soc/hisilicon/Makefile +++ b/drivers/soc/hisilicon/Makefile @@ -5,5 +5,4 @@ obj-$(CONFIG_HISI_HBMDEV) += hisi_hbmdev.o obj-$(CONFIG_HISI_HBMCACHE) += hisi_hbmcache.o obj-$(CONFIG_HISI_SOC_CACHE) += hisi_soc_cache_framework.o -obj-$(CONFIG_HISI_SOC_HHA) += hisi_soc_hha.o obj-$(CONFIG_HISI_SOC_L3C) += hisi_soc_l3c.o diff --git a/drivers/soc/hisilicon/hisi_soc_cache_framework.c b/drivers/soc/hisilicon/hisi_soc_cache_framework.c index fc19f72e2843..669caff157dc 100644 --- a/drivers/soc/hisilicon/hisi_soc_cache_framework.c +++ b/drivers/soc/hisilicon/hisi_soc_cache_framework.c @@ -119,60 +119,6 @@ static int hisi_soc_cache_unlock(int cpu, phys_addr_t addr) return ret; } -int hisi_soc_cache_maintain(phys_addr_t addr, size_t size, - enum hisi_soc_cache_maint_type mnt_type) -{ - struct hisi_soc_comp_inst *inst; - struct list_head *head; - int ret = -EOPNOTSUPP; - - if (mnt_type >= HISI_CACHE_MAINT_MAX) - return -EINVAL; - - guard(spinlock)(&soc_cache_devs[HISI_SOC_HHA].lock); - - head = &soc_cache_devs[HISI_SOC_HHA].node; - list_for_each_entry(inst, head, node) { - ret = inst->comp->ops->do_maintain(inst->comp, addr, size, - mnt_type); - if (ret) - return ret; - } - - list_for_each_entry(inst, head, node) { - ret = inst->comp->ops->poll_maintain_done(inst->comp, addr, - size, mnt_type); - if (ret) - return ret; - } - - return ret; -} -EXPORT_SYMBOL_GPL(hisi_soc_cache_maintain); - -static int hisi_soc_cache_maint_pte_entry(pte_t *pte, unsigned long addr, - unsigned long next, struct mm_walk *walk) -{ -#ifdef HISI_SOC_CACHE_LLT - struct hisi_soc_cache_ioctl_param *param = walk->priv; -#else - struct hisi_soc_cache_ioctl_param *param = walk->private; -#endif - size_t size = min(next - addr, param->size + param->addr - addr); - unsigned long offset = offset_in_page(max(addr, param->addr)); - phys_addr_t paddr = PFN_PHYS(pte_pfn(*pte)) + offset; - - if (!pte_present(ptep_get(pte))) - return -EINVAL; - - return hisi_soc_cache_maintain(paddr, size, param->op_type); -} - -static const struct mm_walk_ops hisi_soc_cache_maint_walk = { - .pte_entry = hisi_soc_cache_maint_pte_entry, - .walk_lock = PGWALK_RDLOCK, -}; - static int hisi_soc_cache_inst_check(const struct hisi_soc_comp *comp, enum hisi_soc_comp_type comp_type) { @@ -455,66 +401,8 @@ static int hisi_soc_cache_mmap(struct file *file, struct vm_area_struct *vma) return ret; } -static int __hisi_soc_cache_maintain(struct hisi_soc_cache_ioctl_param *param) -{ - unsigned long start = untagged_addr(param->addr); - struct vm_area_struct *vma; - int ret = 0; - - /* MakeInvalid is not allowed for calls from userspace. */ - if (param->op_type >= HISI_CACHE_MAINT_MAKEINVALID) - return -EINVAL; - - /* Prevent overflow of vaddr + size. */ - if (!param->size || start + param->size < start) - return -EINVAL; - - if (mmap_read_lock_killable(current->mm)) - return -EINTR; - - vma = vma_lookup(current->mm, param->addr); - if (!range_in_vma(vma, start, start + param->size)) { - ret = -EINVAL; - goto out; - } - - /* User should have the write permission of target memory */ - if (!(vma->vm_flags & VM_WRITE)) { - ret = -EINVAL; - goto out; - } - - ret = walk_page_range(current->mm, PAGE_ALIGN_DOWN(start), - PAGE_ALIGN(start + param->size), - &hisi_soc_cache_maint_walk, param); -out: - mmap_read_unlock(current->mm); - return ret; -} - -static long hisi_soc_cache_mgmt_ioctl(struct file *file, u32 cmd, unsigned long arg) -{ - struct hisi_soc_cache_ioctl_param param; - long ret; - - if (copy_from_user(¶m, (void __user *)arg, sizeof(param))) - return -EFAULT; - - switch (cmd) { - case HISI_CACHE_MAINTAIN: - ret = __hisi_soc_cache_maintain(¶m); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - static const struct file_operations soc_cache_dev_fops = { .owner = THIS_MODULE, - .unlocked_ioctl = hisi_soc_cache_mgmt_ioctl, .mmap = hisi_soc_cache_mmap, }; diff --git a/drivers/soc/hisilicon/hisi_soc_cache_framework.h b/drivers/soc/hisilicon/hisi_soc_cache_framework.h index 67ee9a33f382..c711d1f9d75c 100644 --- a/drivers/soc/hisilicon/hisi_soc_cache_framework.h +++ b/drivers/soc/hisilicon/hisi_soc_cache_framework.h @@ -14,8 +14,6 @@ #include <linux/bits.h> #include <linux/types.h> -#include <uapi/misc/hisi_soc_cache/hisi_soc_cache.h> - enum hisi_soc_comp_type { HISI_SOC_L3C, HISI_SOC_HHA, @@ -56,12 +54,6 @@ struct hisi_soc_comp_ops { phys_addr_t addr); int (*poll_unlock_done)(struct hisi_soc_comp *comp, phys_addr_t addr); - int (*do_maintain)(struct hisi_soc_comp *comp, - phys_addr_t addr, size_t size, - enum hisi_soc_cache_maint_type mnt_type); - int (*poll_maintain_done)(struct hisi_soc_comp *comp, - phys_addr_t addr, size_t size, - enum hisi_soc_cache_maint_type mnt_type); }; /** @@ -86,7 +78,5 @@ struct hisi_soc_comp { int hisi_soc_comp_inst_add(struct hisi_soc_comp *comp); int hisi_soc_comp_inst_del(struct hisi_soc_comp *comp); -int hisi_soc_cache_maintain(phys_addr_t addr, size_t size, - enum hisi_soc_cache_maint_type mnt_type); #endif diff --git a/drivers/soc/hisilicon/hisi_soc_hha.c b/drivers/soc/hisilicon/hisi_soc_hha.c deleted file mode 100644 index 22a1ec8b8fc9..000000000000 --- a/drivers/soc/hisilicon/hisi_soc_hha.c +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for HiSilicon Hydra Home Agent (HHA). - * - * Copyright (c) 2024 HiSilicon Technologies Co., Ltd. - * Author: Yicong Yang <yangyicong@hisilicon.com> - * Yushan Wang <wangyushan12@huawei.com> - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/bitfield.h> -#include <linux/cpumask.h> -#include <linux/device.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/mod_devicetable.h> -#include <linux/platform_device.h> -#include <linux/spinlock.h> - -#include "hisi_soc_cache_framework.h" - -#define HISI_HHA_CTRL 0x5004 -#define HISI_HHA_CTRL_EN BIT(0) -#define HISI_HHA_CTRL_RANGE BIT(1) -#define HISI_HHA_CTRL_TYPE GENMASK(3, 2) -#define HISI_HHA_START_L 0x5008 -#define HISI_HHA_START_H 0x500c -#define HISI_HHA_LEN_L 0x5010 -#define HISI_HHA_LEN_H 0x5014 - -/* The maintain operation performs in a 128 Byte granularity */ -#define HISI_HHA_MAINT_ALIGN 128 - -#define HISI_HHA_POLL_GAP_US 10 - -struct hisi_soc_hha { - struct hisi_soc_comp comp; - /* Locks HHA instance to forbid overlapping access. */ - spinlock_t lock; - struct device *dev; - void __iomem *base; -}; - -static bool hisi_hha_cache_maintain_wait_finished(struct hisi_soc_hha *soc_hha) -{ - u32 val; - - return !readl_poll_timeout_atomic(soc_hha->base + HISI_HHA_CTRL, val, - !(val & HISI_HHA_CTRL_EN), - HISI_HHA_POLL_GAP_US, - jiffies_to_usecs(HZ)); -} - -static int hisi_hha_cache_do_maintain(struct hisi_soc_comp *comp, - phys_addr_t addr, size_t size, - enum hisi_soc_cache_maint_type mnt_type) -{ - struct hisi_soc_hha *soc_hha = container_of(comp, struct hisi_soc_hha, - comp); - phys_addr_t top; - int ret = 0; - u32 reg; - - if (!size) - return -EINVAL; - - addr = ALIGN_DOWN(addr, HISI_HHA_MAINT_ALIGN); - top = ALIGN(addr + size, HISI_HHA_MAINT_ALIGN); - size = top - addr; - - if (mnt_type < 0 || mnt_type >= HISI_CACHE_MAINT_MAX) - return -EOPNOTSUPP; - - /* - * Hardware will search for addresses ranging [addr, addr + size -1], - * last byte included, and perform maintain in 128 byte granule - * on those which contain the addresses. - */ - size -= 1; - - guard(spinlock)(&soc_hha->lock); - - if (!hisi_hha_cache_maintain_wait_finished(soc_hha)) - return -EBUSY; - - writel(lower_32_bits(addr), soc_hha->base + HISI_HHA_START_L); - writel(upper_32_bits(addr), soc_hha->base + HISI_HHA_START_H); - writel(lower_32_bits(size), soc_hha->base + HISI_HHA_LEN_L); - writel(upper_32_bits(size), soc_hha->base + HISI_HHA_LEN_H); - - reg = FIELD_PREP(HISI_HHA_CTRL_TYPE, mnt_type); - reg |= HISI_HHA_CTRL_RANGE | HISI_HHA_CTRL_EN; - writel(reg, soc_hha->base + HISI_HHA_CTRL); - - return ret; -} - -static int hisi_hha_cache_poll_maintain_done(struct hisi_soc_comp *comp, - phys_addr_t addr, size_t size, - enum hisi_soc_cache_maint_type mnt_type) -{ - struct hisi_soc_hha *soc_hha = container_of(comp, struct hisi_soc_hha, - comp); - - guard(spinlock)(&soc_hha->lock); - - if (!hisi_hha_cache_maintain_wait_finished(soc_hha)) - return -ETIMEDOUT; - - return 0; -} - -static struct hisi_soc_comp_ops hisi_soc_hha_comp_ops = { - .do_maintain = hisi_hha_cache_do_maintain, - .poll_maintain_done = hisi_hha_cache_poll_maintain_done, -}; - -static void hisi_hha_comp_inst_del(void *priv) -{ - struct hisi_soc_hha *soc_hha = priv; - - hisi_soc_comp_inst_del(&soc_hha->comp); -} - -static int hisi_soc_hha_probe(struct platform_device *pdev) -{ - struct hisi_soc_hha *soc_hha; - struct resource *mem; - int ret; - - soc_hha = devm_kzalloc(&pdev->dev, sizeof(*soc_hha), GFP_KERNEL); - if (!soc_hha) - return -ENOMEM; - - platform_set_drvdata(pdev, soc_hha); - soc_hha->dev = &pdev->dev; - - spin_lock_init(&soc_hha->lock); - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) - return -ENODEV; - - /* - * HHA cache driver share the same register region with HHA 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_hha->base = devm_ioremap(&pdev->dev, mem->start, - resource_size(mem)); - if (IS_ERR_OR_NULL(soc_hha->base)) { - return dev_err_probe(&pdev->dev, PTR_ERR(soc_hha->base), - "failed to remap io memory"); - } - - soc_hha->comp.ops = &hisi_soc_hha_comp_ops; - soc_hha->comp.comp_type = BIT(HISI_SOC_HHA); - cpumask_copy(&soc_hha->comp.affinity_mask, cpu_possible_mask); - - ret = hisi_soc_comp_inst_add(&soc_hha->comp); - if (ret) - return dev_err_probe(&pdev->dev, ret, - "failed to register maintain inst"); - - return devm_add_action_or_reset(&pdev->dev, hisi_hha_comp_inst_del, - soc_hha); -} - -static const struct acpi_device_id hisi_soc_hha_ids[] = { - { "HISI0511", }, - { } -}; -MODULE_DEVICE_TABLE(acpi, hisi_soc_hha_ids); - -static struct platform_driver hisi_soc_hha_driver = { - .driver = { - .name = "hisi_soc_hha", - .acpi_match_table = hisi_soc_hha_ids, - }, - .probe = hisi_soc_hha_probe, -}; - -module_platform_driver(hisi_soc_hha_driver); - -MODULE_DESCRIPTION("Hisilicon Hydra Home Agent driver supporting cache maintenance"); -MODULE_AUTHOR("Yicong Yang <yangyicong@hisilicon.com>"); -MODULE_AUTHOR("Yushan Wang <wangyushan12@huawei.com>"); -MODULE_LICENSE("GPL"); diff --git a/include/uapi/misc/hisi_soc_cache/hisi_soc_cache.h b/include/uapi/misc/hisi_soc_cache/hisi_soc_cache.h deleted file mode 100644 index 8b190941c805..000000000000 --- a/include/uapi/misc/hisi_soc_cache/hisi_soc_cache.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */ -/* Copyright (c) 2024-2024 HiSilicon Limited. */ -#ifndef _UAPI_HISI_SOC_CACHE_H -#define _UAPI_HISI_SOC_CACHE_H - -#include <linux/types.h> - -/* HISI_CACHE_MAINTAIN: cache maintain operation for HiSilicon SoC */ -#define HISI_CACHE_MAINTAIN _IOW('C', 1, unsigned long) - -/* - * Further information of these operations can be found at: - * https://developer.arm.com/documentation/ihi0050/latest/ - */ -enum hisi_soc_cache_maint_type { - HISI_CACHE_MAINT_CLEANSHARED, - HISI_CACHE_MAINT_CLEANINVALID, -#ifdef __KERNEL__ - HISI_CACHE_MAINT_MAKEINVALID, -#endif - - HISI_CACHE_MAINT_MAX -}; - -/** - * struct hisi_soc_cache_ioctl_param - User data for hisi cache operates. - * @op_type: cache maintain type - * @addr: cache maintain address - * @size: cache maintain size - */ -struct hisi_soc_cache_ioctl_param { - enum hisi_soc_cache_maint_type op_type; - unsigned long addr; - unsigned long size; -}; - -#endif -- 2.33.0
From: Jonathan Cameron <Jonathan.Cameron@huawei.com> driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10043 ---------------------------------------------------------------------- The res_desc parameter was originally introduced for documentation purposes and with the idea that with HDM-DB CXL invalidation could be triggered from the device. That has not come to pass and the continued existence of the option is confusing when we add a range in the following patch which might not be a strict subset of the res_desc. So avoid that confusion by dropping the parameter. Link: https://lore.kernel.org/linux-mm/686eedb25ed02_24471002e@dwillia2-xfh.jf.int... Reviewed-by: Dan Williams <dan.j.williams@intel.com> Suggested-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Conor Dooley <conor.dooley@microchip.com> Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- arch/x86/mm/pat/set_memory.c | 2 +- drivers/cxl/core/region.c | 2 +- drivers/nvdimm/region.c | 2 +- drivers/nvdimm/region_devs.c | 2 +- include/linux/memregion.h | 7 +++---- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c index d0aaa967cbc0..ffed9a32100d 100644 --- a/arch/x86/mm/pat/set_memory.c +++ b/arch/x86/mm/pat/set_memory.c @@ -363,7 +363,7 @@ bool cpu_cache_has_invalidate_memregion(void) } EXPORT_SYMBOL_NS_GPL(cpu_cache_has_invalidate_memregion, DEVMEM); -int cpu_cache_invalidate_memregion(int res_desc) +int cpu_cache_invalidate_memregion(void) { if (WARN_ON_ONCE(!cpu_cache_has_invalidate_memregion())) return -ENXIO; diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 9efe9539ec64..46cd50f2e2d8 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -134,7 +134,7 @@ static int cxl_region_invalidate_memregion(struct cxl_region *cxlr) } } - cpu_cache_invalidate_memregion(IORES_DESC_CXL); + cpu_cache_invalidate_memregion(); return 0; } diff --git a/drivers/nvdimm/region.c b/drivers/nvdimm/region.c index 88dc062af5f8..c43506448edf 100644 --- a/drivers/nvdimm/region.c +++ b/drivers/nvdimm/region.c @@ -110,7 +110,7 @@ static void nd_region_remove(struct device *dev) * here is ok. */ if (cpu_cache_has_invalidate_memregion()) - cpu_cache_invalidate_memregion(IORES_DESC_PERSISTENT_MEMORY); + cpu_cache_invalidate_memregion(); } static int child_notify(struct device *dev, void *data) diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index c13264c7b0de..2f9305b08cf9 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -90,7 +90,7 @@ static int nd_region_invalidate_memregion(struct nd_region *nd_region) } } - cpu_cache_invalidate_memregion(IORES_DESC_PERSISTENT_MEMORY); + cpu_cache_invalidate_memregion(); out: for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; diff --git a/include/linux/memregion.h b/include/linux/memregion.h index c01321467789..945646bde825 100644 --- a/include/linux/memregion.h +++ b/include/linux/memregion.h @@ -26,8 +26,7 @@ static inline void memregion_free(int id) /** * cpu_cache_invalidate_memregion - drop any CPU cached data for - * memregions described by @res_desc - * @res_desc: one of the IORES_DESC_* types + * memregion * * Perform cache maintenance after a memory event / operation that * changes the contents of physical memory in a cache-incoherent manner. @@ -46,7 +45,7 @@ static inline void memregion_free(int id) * the cache maintenance. */ #ifdef CONFIG_ARCH_HAS_CPU_CACHE_INVALIDATE_MEMREGION -int cpu_cache_invalidate_memregion(int res_desc); +int cpu_cache_invalidate_memregion(void); bool cpu_cache_has_invalidate_memregion(void); #else static inline bool cpu_cache_has_invalidate_memregion(void) @@ -54,7 +53,7 @@ static inline bool cpu_cache_has_invalidate_memregion(void) return false; } -static inline int cpu_cache_invalidate_memregion(int res_desc) +static inline int cpu_cache_invalidate_memregion(void) { WARN_ON_ONCE("CPU cache invalidation required"); return -ENXIO; -- 2.33.0
From: Yicong Yang <yangyicong@hisilicon.com> driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10043 ---------------------------------------------------------------------- Extend cpu_cache_invalidate_memregion() to support invalidating a particular range of memory by introducing start and length parameters. Control of types of invalidation is left for when use cases turn up. For now everything is Clean and Invalidate. Where the range is unknown, use the provided cpu_cache_invalidate_all() helper to act as documentation of intent in a fashion that is clearer than passing (0, -1) to cpu_cache_invalidate_memregion(). Signed-off-by: Yicong Yang <yangyicong@hisilicon.com> Reviewed-by: Dan Williams <dan.j.williams@intel.com> Acked-by: Davidlohr Bueso <dave@stgolabs.net> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Conor Dooley <conor.dooley@microchip.com> Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- arch/x86/mm/pat/set_memory.c | 2 +- drivers/cxl/core/region.c | 5 ++++- drivers/nvdimm/region.c | 2 +- drivers/nvdimm/region_devs.c | 2 +- include/linux/memregion.h | 13 +++++++++++-- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c index ffed9a32100d..7ae8b3636683 100644 --- a/arch/x86/mm/pat/set_memory.c +++ b/arch/x86/mm/pat/set_memory.c @@ -363,7 +363,7 @@ bool cpu_cache_has_invalidate_memregion(void) } EXPORT_SYMBOL_NS_GPL(cpu_cache_has_invalidate_memregion, DEVMEM); -int cpu_cache_invalidate_memregion(void) +int cpu_cache_invalidate_memregion(phys_addr_t start, size_t len) { if (WARN_ON_ONCE(!cpu_cache_has_invalidate_memregion())) return -ENXIO; diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 46cd50f2e2d8..760c1d4335fb 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -134,7 +134,10 @@ static int cxl_region_invalidate_memregion(struct cxl_region *cxlr) } } - cpu_cache_invalidate_memregion(); + if (!cxlr->params.res) + return -ENXIO; + cpu_cache_invalidate_memregion(cxlr->params.res->start, + resource_size(cxlr->params.res)); return 0; } diff --git a/drivers/nvdimm/region.c b/drivers/nvdimm/region.c index c43506448edf..42e982db5b04 100644 --- a/drivers/nvdimm/region.c +++ b/drivers/nvdimm/region.c @@ -110,7 +110,7 @@ static void nd_region_remove(struct device *dev) * here is ok. */ if (cpu_cache_has_invalidate_memregion()) - cpu_cache_invalidate_memregion(); + cpu_cache_invalidate_all(); } static int child_notify(struct device *dev, void *data) diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 2f9305b08cf9..922863759af1 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -90,7 +90,7 @@ static int nd_region_invalidate_memregion(struct nd_region *nd_region) } } - cpu_cache_invalidate_memregion(); + cpu_cache_invalidate_all(); out: for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; diff --git a/include/linux/memregion.h b/include/linux/memregion.h index 945646bde825..a55f62cc5266 100644 --- a/include/linux/memregion.h +++ b/include/linux/memregion.h @@ -27,6 +27,9 @@ static inline void memregion_free(int id) /** * cpu_cache_invalidate_memregion - drop any CPU cached data for * memregion + * @start: start physical address of the target memory region. + * @len: length of the target memory region. -1 for all the regions of + * the target type. * * Perform cache maintenance after a memory event / operation that * changes the contents of physical memory in a cache-incoherent manner. @@ -45,7 +48,7 @@ static inline void memregion_free(int id) * the cache maintenance. */ #ifdef CONFIG_ARCH_HAS_CPU_CACHE_INVALIDATE_MEMREGION -int cpu_cache_invalidate_memregion(void); +int cpu_cache_invalidate_memregion(phys_addr_t start, size_t len); bool cpu_cache_has_invalidate_memregion(void); #else static inline bool cpu_cache_has_invalidate_memregion(void) @@ -53,10 +56,16 @@ static inline bool cpu_cache_has_invalidate_memregion(void) return false; } -static inline int cpu_cache_invalidate_memregion(void) +static inline int cpu_cache_invalidate_memregion(phys_addr_t start, size_t len) { WARN_ON_ONCE("CPU cache invalidation required"); return -ENXIO; } #endif + +static inline int cpu_cache_invalidate_all(void) +{ + return cpu_cache_invalidate_memregion(0, -1); +} + #endif /* _MEMREGION_H_ */ -- 2.33.0
From: Yicong Yang <yangyicong@hisilicon.com> driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10043 ---------------------------------------------------------------------- ARCH_HAS_CPU_CACHE_INVALIDATE_MEMREGION provides the mechanism for invalidating certain memory regions in a cache-incoherent manner. Currently this is used by NVDIMM and CXL memory drivers in cases where it is necessary to flush all data from caches by physical address range. The operations in question are effectively memory hotplug, where stale data might otherwise remain in the caches. This is separate from the invalidates done to enable use of non-coherent DMA masters, primarily in terms of when it is needed (not related to DMA mappings) and how deep the flush must push data. The flushes done for non-coherent DMA only need to reach the Point of Coherence of a single host (which is often nearer CPUs and DMA masters than the physical storage). This operation must push the data out of non architectural caches (memory-side caches, write buffers etc) and typically all the way to the memory device. In some architectures these operations are supported by system components that may become available only later in boot as they are either present on a discoverable bus, or via a firmware description of an MMIO interface (e.g. ACPI DSDT). Provide a framework to handle this case. Architectures can opt in for this support via CONFIG_GENERIC_CPU_CACHE_MAINTENANCE Add a registration framework. Each driver provides an ops structure and the first op is Write Back and Invalidate by PA Range. The driver may over invalidate. For systems that can perform this operation asynchronously an optional completion check operation is also provided. If present that must be called to ensure that the action has finished. This provides a considerable performance advantage if multiple agents are involved in the maintenance operation. When multiple agents are present in the system each should register with this framework and the core code will issue the invalidate to all of them before checking for completion on each. This is done to avoid need for filtering in the core code which can become complex when interleave, potentially across different cache coherency hardware is going on, so it is easier to tell everyone and let those who don't care do nothing. Signed-off-by: Yicong Yang <yangyicong@hisilicon.com> Co-developed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Acked-by: Conor Dooley <conor.dooley@microchip.com> Signed-off-by: Conor Dooley <conor.dooley@microchip.com> Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- include/linux/cache_coherency.h | 61 ++++++++++++++ lib/Kconfig | 3 + lib/Makefile | 2 + lib/cache_maint.c | 138 ++++++++++++++++++++++++++++++++ 4 files changed, 204 insertions(+) create mode 100644 include/linux/cache_coherency.h create mode 100644 lib/cache_maint.c diff --git a/include/linux/cache_coherency.h b/include/linux/cache_coherency.h new file mode 100644 index 000000000000..cc81c5733e31 --- /dev/null +++ b/include/linux/cache_coherency.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cache coherency maintenance operation device drivers + * + * Copyright Huawei 2025 + */ +#ifndef _LINUX_CACHE_COHERENCY_H_ +#define _LINUX_CACHE_COHERENCY_H_ + +#include <linux/list.h> +#include <linux/kref.h> +#include <linux/types.h> + +struct cc_inval_params { + phys_addr_t addr; + size_t size; +}; + +struct cache_coherency_ops_inst; + +struct cache_coherency_ops { + int (*wbinv)(struct cache_coherency_ops_inst *cci, + struct cc_inval_params *invp); + int (*done)(struct cache_coherency_ops_inst *cci); +}; + +struct cache_coherency_ops_inst { + struct kref kref; + struct list_head node; + const struct cache_coherency_ops *ops; +}; + +int cache_coherency_ops_instance_register(struct cache_coherency_ops_inst *cci); +void cache_coherency_ops_instance_unregister(struct cache_coherency_ops_inst *cci); + +struct cache_coherency_ops_inst * +_cache_coherency_ops_instance_alloc(const struct cache_coherency_ops *ops, + size_t size); +/** + * cache_coherency_ops_instance_alloc - Allocate cache coherency ops instance + * @ops: Cache maintenance operations + * @drv_struct: structure that contains the struct cache_coherency_ops_inst + * @member: Name of the struct cache_coherency_ops_inst member in @drv_struct. + * + * This allocates a driver specific structure and initializes the + * cache_coherency_ops_inst embedded in the drv_struct. Upon success the + * pointer must be freed via cache_coherency_ops_instance_put(). + * + * Returns a &drv_struct * on success, %NULL on error. + */ +#define cache_coherency_ops_instance_alloc(ops, drv_struct, member) \ + ({ \ + static_assert(__same_type(struct cache_coherency_ops_inst, \ + ((drv_struct *)NULL)->member)); \ + static_assert(offsetof(drv_struct, member) == 0); \ + (drv_struct *)_cache_coherency_ops_instance_alloc(ops, \ + sizeof(drv_struct)); \ + }) +void cache_coherency_ops_instance_put(struct cache_coherency_ops_inst *cci); + +#endif diff --git a/lib/Kconfig b/lib/Kconfig index 43d69669465a..5b06ae12ac0c 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -679,6 +679,9 @@ config MEMREGION config ARCH_HAS_CPU_CACHE_INVALIDATE_MEMREGION bool +config GENERIC_CPU_CACHE_MAINTENANCE + bool + config ARCH_HAS_MEMREMAP_COMPAT_ALIGN bool diff --git a/lib/Makefile b/lib/Makefile index c5281542aaa9..2bd1f25ccd61 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -157,6 +157,8 @@ obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o obj-$(CONFIG_CHECK_SIGNATURE) += check_signature.o obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o +obj-$(CONFIG_GENERIC_CPU_CACHE_MAINTENANCE) += cache_maint.o + lib-y += logic_pio.o lib-$(CONFIG_INDIRECT_IOMEM) += logic_iomem.o diff --git a/lib/cache_maint.c b/lib/cache_maint.c new file mode 100644 index 000000000000..34cc78b70d5b --- /dev/null +++ b/lib/cache_maint.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic support for Memory System Cache Maintenance operations. + * + * Coherency maintenance drivers register with this simple framework that will + * iterate over each registered instance to first kick off invalidation and + * then to wait until it is complete. + * + * If no implementations are registered yet cpu_cache_has_invalidate_memregion() + * will return false. If this runs concurrently with unregistration then a + * race exists but this is no worse than the case where the operations instance + * responsible for a given memory region has not yet registered. + */ +#include <linux/cache_coherency.h> +#include <linux/cleanup.h> +#include <linux/container_of.h> +#include <linux/export.h> +#include <linux/kref.h> +#include <linux/list.h> +#include <linux/memregion.h> +#include <linux/module.h> +#include <linux/rwsem.h> +#include <linux/slab.h> + +static LIST_HEAD(cache_ops_instance_list); +static DECLARE_RWSEM(cache_ops_instance_list_lock); + +static void __cache_coherency_ops_instance_free(struct kref *kref) +{ + struct cache_coherency_ops_inst *cci = + container_of(kref, struct cache_coherency_ops_inst, kref); + kfree(cci); +} + +void cache_coherency_ops_instance_put(struct cache_coherency_ops_inst *cci) +{ + kref_put(&cci->kref, __cache_coherency_ops_instance_free); +} +EXPORT_SYMBOL_GPL(cache_coherency_ops_instance_put); + +static int cache_inval_one(struct cache_coherency_ops_inst *cci, void *data) +{ + if (!cci->ops) + return -EINVAL; + + return cci->ops->wbinv(cci, data); +} + +static int cache_inval_done_one(struct cache_coherency_ops_inst *cci) +{ + if (!cci->ops) + return -EINVAL; + + if (!cci->ops->done) + return 0; + + return cci->ops->done(cci); +} + +static int cache_invalidate_memregion(phys_addr_t addr, size_t size) +{ + int ret; + struct cache_coherency_ops_inst *cci; + struct cc_inval_params params = { + .addr = addr, + .size = size, + }; + + guard(rwsem_read)(&cache_ops_instance_list_lock); + list_for_each_entry(cci, &cache_ops_instance_list, node) { + ret = cache_inval_one(cci, ¶ms); + if (ret) + return ret; + } + list_for_each_entry(cci, &cache_ops_instance_list, node) { + ret = cache_inval_done_one(cci); + if (ret) + return ret; + } + + return 0; +} + +struct cache_coherency_ops_inst * +_cache_coherency_ops_instance_alloc(const struct cache_coherency_ops *ops, + size_t size) +{ + struct cache_coherency_ops_inst *cci; + + if (!ops || !ops->wbinv) + return NULL; + + cci = kzalloc(size, GFP_KERNEL); + if (!cci) + return NULL; + + cci->ops = ops; + INIT_LIST_HEAD(&cci->node); + kref_init(&cci->kref); + + return cci; +} +EXPORT_SYMBOL_NS_GPL(_cache_coherency_ops_instance_alloc, CACHE_COHERENCY); + +int cache_coherency_ops_instance_register(struct cache_coherency_ops_inst *cci) +{ + guard(rwsem_write)(&cache_ops_instance_list_lock); + list_add(&cci->node, &cache_ops_instance_list); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_register, CACHE_COHERENCY); + +void cache_coherency_ops_instance_unregister(struct cache_coherency_ops_inst *cci) +{ + guard(rwsem_write)(&cache_ops_instance_list_lock); + list_del(&cci->node); +} +EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_unregister, CACHE_COHERENCY); + +int cpu_cache_invalidate_memregion(phys_addr_t start, size_t len) +{ + return cache_invalidate_memregion(start, len); +} +EXPORT_SYMBOL_NS_GPL(cpu_cache_invalidate_memregion, DEVMEM); + +/* + * Used for optimization / debug purposes only as removal can race + * + * Machines that do not support invalidation, e.g. VMs, will not have any + * operations instance to register and so this will always return false. + */ +bool cpu_cache_has_invalidate_memregion(void) +{ + guard(rwsem_read)(&cache_ops_instance_list_lock); + return !list_empty(&cache_ops_instance_list); +} +EXPORT_SYMBOL_NS_GPL(cpu_cache_has_invalidate_memregion, DEVMEM); -- 2.33.0
From: Jonathan Cameron <Jonathan.Cameron@huawei.com> driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10043 ---------------------------------------------------------------------- The generic CPU cache maintenance framework provides a way to register drivers for devices implementing the underlying support for cpu_cache_has_invalidate_memregion(). Enable it for arm64 by selecting GENERIC_CPU_CACHE_MAINTENANCE which provides the implementation for, and in turn selects, ARCH_HAS_CPU_CACHE_INVALIDATE_MEMREGION. Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Conor Dooley <conor.dooley@microchip.com> Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- arch/arm64/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 736908afc98a..42a589209ceb 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -21,6 +21,7 @@ config ARM64 select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE select ARCH_HAS_CACHE_LINE_SIZE select ARCH_HAS_CC_PLATFORM + select ARCH_HAS_CPU_CACHE_INVALIDATE_MEMREGION select ARCH_HAS_COPY_MC if ACPI_APEI_GHES select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL @@ -147,6 +148,7 @@ config ARM64 select GENERIC_ARCH_TOPOLOGY select GENERIC_CLOCKEVENTS_BROADCAST select GENERIC_CPU_AUTOPROBE + select GENERIC_CPU_CACHE_MAINTENANCE select GENERIC_CPU_DEVICES select GENERIC_CPU_VULNERABILITIES select GENERIC_EARLY_IOREMAP -- 2.33.0
From: Jonathan Cameron <Jonathan.Cameron@huawei.com> driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10043 ---------------------------------------------------------------------- Seems unfair to inflict the cache-coherency drivers on Conor with out also stepping up as a second maintainer for drivers/cache. Include the library support for cache-coherency maintenance drivers to the existing entry. Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Acked-by: Conor Dooley <conor.dooley@microchip.com> Signed-off-by: Conor Dooley <conor.dooley@microchip.com> Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- MAINTAINERS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 02c73c264d85..6d2042e1de43 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20651,10 +20651,12 @@ F: drivers/staging/ STANDALONE CACHE CONTROLLER DRIVERS M: Conor Dooley <conor@kernel.org> +M: Jonathan Cameron <jonathan.cameron@huawei.com> L: linux-riscv@lists.infradead.org S: Maintained T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/ -F: drivers/cache +F: include/cache_coherency.h +F: lib/cache_maint.c STARFIRE/DURALAN NETWORK DRIVER M: Ion Badulescu <ionut@badula.org> -- 2.33.0
From: Jonathan Cameron <Jonathan.Cameron@huawei.com> driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10043 ---------------------------------------------------------------------- The next patch will add a new type of cache maintenance driver responsible for flushing deeper than is necessary for non coherent DMA (current use case of drivers/cache drivers), as needed when performing operations such as memory hotplug and security unlocking of persistent memory. The two types of operation are similar enough to share a drivers/cache directory and MAINTAINERS but are otherwise currently unrelated. To avoid confusion have two separate menus. Each has dependencies that are implemented by making them boolean symbols, here CACHEMAINT_FOR_DMA which is dependent on RISCV as all driver are currently for platforms of that architecture. Set new symbol default to y to avoid breaking existing configs. This has no affect on actual code built, just visibility of the menu. Suggested-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Conor Dooley <conor.dooley@microchip.com> Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/cache/Kconfig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/cache/Kconfig b/drivers/cache/Kconfig index d6e5e3abaad8..79616b476d7b 100644 --- a/drivers/cache/Kconfig +++ b/drivers/cache/Kconfig @@ -1,11 +1,15 @@ # SPDX-License-Identifier: GPL-2.0 -menu "Cache Drivers" + +menuconfig CACHEMAINT_FOR_DMA + bool "Cache management for noncoherent DMA" + depends on RISCV + default y + help + These drivers implement support for noncoherent DMA master devices + on platforms that lack the standard CPU interfaces for this. config AX45MP_L2_CACHE bool "Andes Technology AX45MP L2 Cache controller" - depends on RISCV select RISCV_NONSTANDARD_CACHE_OPS help Support for the L2 cache controller on Andes Technology AX45MP platforms. - -endmenu -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10043 ---------------------------------------------------------------------- Hydra Home Agent is a device used to maintain cache coherency. Add support for explicit cache maintenance operations using it. A system has multiple of these agents. Whilst only one agent is responsible for a given cache line, interleave means that for a range operation, responsibility for the cache lines making up the range will typically be spread across multiple instances. Put this driver on a new Kconfig menu under drivers/cache. The short description as memory hotplug like operations is intended to cover the somewhat complex set of cases where this unit applies and differentiate it clearly from typical non coherent DMA flows. Co-developed-by: Yicong Yang <yangyicong@hisilicon.com> Signed-off-by: Yicong Yang <yangyicong@hisilicon.com> Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Conor Dooley <conor.dooley@microchip.com> Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/cache/Kconfig | 18 ++++ drivers/cache/Makefile | 2 + drivers/cache/hisi_soc_hha.c | 196 +++++++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 drivers/cache/hisi_soc_hha.c diff --git a/drivers/cache/Kconfig b/drivers/cache/Kconfig index 79616b476d7b..1137d8947198 100644 --- a/drivers/cache/Kconfig +++ b/drivers/cache/Kconfig @@ -13,3 +13,21 @@ config AX45MP_L2_CACHE select RISCV_NONSTANDARD_CACHE_OPS help Support for the L2 cache controller on Andes Technology AX45MP platforms. + +menuconfig CACHEMAINT_FOR_HOTPLUG + bool "Cache management for memory hot plug like operations" + depends on GENERIC_CPU_CACHE_MAINTENANCE + help + These drivers implement cache management for flows where it is necessary + to flush data from all host caches. + +config HISI_SOC_HHA + tristate "HiSilicon Hydra Home Agent (HHA) device driver" + depends on (ARM64 && ACPI) || COMPILE_TEST + help + The Hydra Home Agent (HHA) is responsible for cache coherency + on the SoC. This drivers enables the cache maintenance functions of + the HHA. + + This driver can be built as a module. If so, the module will be + called hisi_soc_hha. diff --git a/drivers/cache/Makefile b/drivers/cache/Makefile index 2012e7fb978d..bbf3c52fb3db 100644 --- a/drivers/cache/Makefile +++ b/drivers/cache/Makefile @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_AX45MP_L2_CACHE) += ax45mp_cache.o + +obj-$(CONFIG_HISI_SOC_HHA) += hisi_soc_hha.o diff --git a/drivers/cache/hisi_soc_hha.c b/drivers/cache/hisi_soc_hha.c new file mode 100644 index 000000000000..8c0b4a6b0df7 --- /dev/null +++ b/drivers/cache/hisi_soc_hha.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for HiSilicon Hydra Home Agent (HHA). + * + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. + * Author: Yicong Yang <yangyicong@hisilicon.com> + * Yushan Wang <wangyushan12@huawei.com> + * + * A system typically contains multiple HHAs. Each is responsible for a subset + * of the physical addresses in the system, but interleave can make the mapping + * from a particular cache line to a responsible HHA complex. As such no + * filtering is done in the driver, with the hardware being responsible for + * responding with success for even if it was not responsible for any addresses + * in the range on which the operation was requested. + */ + +#include <linux/bitfield.h> +#include <linux/cache_coherency.h> +#include <linux/dev_printk.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/memregion.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> + +#define HISI_HHA_CTRL 0x5004 +#define HISI_HHA_CTRL_EN BIT(0) +#define HISI_HHA_CTRL_RANGE BIT(1) +#define HISI_HHA_CTRL_TYPE GENMASK(3, 2) +#define HISI_HHA_START_L 0x5008 +#define HISI_HHA_START_H 0x500c +#define HISI_HHA_LEN_L 0x5010 +#define HISI_HHA_LEN_H 0x5014 + +/* The maintain operation performs in a 128 Byte granularity */ +#define HISI_HHA_MAINT_ALIGN 128 + +#define HISI_HHA_POLL_GAP_US 10 +#define HISI_HHA_POLL_TIMEOUT_US 50000 + +struct hisi_soc_hha { + /* Must be first element */ + struct cache_coherency_ops_inst cci; + /* Locks HHA instance to forbid overlapping access. */ + struct mutex lock; + void __iomem *base; +}; + +static bool hisi_hha_cache_maintain_wait_finished(struct hisi_soc_hha *soc_hha) +{ + u32 val; + + return !readl_poll_timeout_atomic(soc_hha->base + HISI_HHA_CTRL, val, + !(val & HISI_HHA_CTRL_EN), + HISI_HHA_POLL_GAP_US, + HISI_HHA_POLL_TIMEOUT_US); +} + +static int hisi_soc_hha_wbinv(struct cache_coherency_ops_inst *cci, + struct cc_inval_params *invp) +{ + struct hisi_soc_hha *soc_hha = + container_of(cci, struct hisi_soc_hha, cci); + phys_addr_t top, addr = invp->addr; + size_t size = invp->size; + u32 reg; + + if (!size) + return -EINVAL; + + addr = ALIGN_DOWN(addr, HISI_HHA_MAINT_ALIGN); + top = ALIGN(addr + size, HISI_HHA_MAINT_ALIGN); + size = top - addr; + + guard(mutex)(&soc_hha->lock); + + if (!hisi_hha_cache_maintain_wait_finished(soc_hha)) + return -EBUSY; + + /* + * Hardware will search for addresses ranging [addr, addr + size - 1], + * last byte included, and perform maintenance in 128 byte granules + * on those cachelines which contain the addresses. If a given instance + * is either not responsible for a cacheline or that cacheline is not + * currently present then the search will fail, no operation will be + * necessary and the device will report success. + */ + size -= 1; + + writel(lower_32_bits(addr), soc_hha->base + HISI_HHA_START_L); + writel(upper_32_bits(addr), soc_hha->base + HISI_HHA_START_H); + writel(lower_32_bits(size), soc_hha->base + HISI_HHA_LEN_L); + writel(upper_32_bits(size), soc_hha->base + HISI_HHA_LEN_H); + + reg = FIELD_PREP(HISI_HHA_CTRL_TYPE, 1); /* Clean Invalid */ + reg |= HISI_HHA_CTRL_RANGE | HISI_HHA_CTRL_EN; + writel(reg, soc_hha->base + HISI_HHA_CTRL); + + return 0; +} + +static int hisi_soc_hha_done(struct cache_coherency_ops_inst *cci) +{ + struct hisi_soc_hha *soc_hha = + container_of(cci, struct hisi_soc_hha, cci); + + guard(mutex)(&soc_hha->lock); + if (!hisi_hha_cache_maintain_wait_finished(soc_hha)) + return -ETIMEDOUT; + + return 0; +} + +static const struct cache_coherency_ops hha_ops = { + .wbinv = hisi_soc_hha_wbinv, + .done = hisi_soc_hha_done, +}; + +static int hisi_soc_hha_probe(struct platform_device *pdev) +{ + struct hisi_soc_hha *soc_hha; + struct resource *mem; + int ret; + + soc_hha = cache_coherency_ops_instance_alloc(&hha_ops, + struct hisi_soc_hha, cci); + if (!soc_hha) + return -ENOMEM; + + platform_set_drvdata(pdev, soc_hha); + + mutex_init(&soc_hha->lock); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + ret = -ENOMEM; + goto err_free_cci; + } + + soc_hha->base = ioremap(mem->start, resource_size(mem)); + if (!soc_hha->base) { + ret = dev_err_probe(&pdev->dev, -ENOMEM, + "failed to remap io memory"); + goto err_free_cci; + } + + ret = cache_coherency_ops_instance_register(&soc_hha->cci); + if (ret) + goto err_iounmap; + + return 0; + +err_iounmap: + iounmap(soc_hha->base); +err_free_cci: + cache_coherency_ops_instance_put(&soc_hha->cci); + return ret; +} + +static int hisi_soc_hha_remove(struct platform_device *pdev) +{ + struct hisi_soc_hha *soc_hha = platform_get_drvdata(pdev); + + cache_coherency_ops_instance_unregister(&soc_hha->cci); + iounmap(soc_hha->base); + cache_coherency_ops_instance_put(&soc_hha->cci); + + return 0; +} + +static const struct acpi_device_id hisi_soc_hha_ids[] = { + { "HISI0511", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hisi_soc_hha_ids); + +static struct platform_driver hisi_soc_hha_driver = { + .driver = { + .name = "hisi_soc_hha", + .acpi_match_table = hisi_soc_hha_ids, + }, + .probe = hisi_soc_hha_probe, + .remove = hisi_soc_hha_remove, +}; + +module_platform_driver(hisi_soc_hha_driver); + +MODULE_IMPORT_NS(CACHE_COHERENCY); +MODULE_DESCRIPTION("HiSilicon Hydra Home Agent driver supporting cache maintenance"); +MODULE_AUTHOR("Yicong Yang <yangyicong@hisilicon.com>"); +MODULE_AUTHOR("Yushan Wang <wangyushan12@huawei.com>"); +MODULE_LICENSE("GPL"); -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10043 ---------------------------------------------------------------------- Add cache maintain related config in response to the refactors of HiSilicon cache maintain framework. Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- arch/arm64/configs/openeuler_defconfig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index 2b90bba63b01..5d30bf3167d9 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -6794,9 +6794,7 @@ CONFIG_SMMU_BYPASS_DEV=y # CONFIG_HISI_HBMDEV is not set # CONFIG_HISI_HBMCACHE is not set CONFIG_KUNPENG_HCCS=m -CONFIG_HISI_SOC_CACHE=y -CONFIG_HISI_SOC_HHA=m -CONFIG_HISI_SOC_L3C=m +CONFIG_HISI_SOC_L3C=y # end of Hisilicon SoC drivers # @@ -8461,3 +8459,8 @@ CONFIG_UB_SENTRY_REMOTE=m # CONFIG_PAGEATTACH=m # end of pageattach + +# cache maintain +CONFIG_CACHEMAINT_FOR_HOTPLUG=y +CONFIG_HISI_SOC_HHA=m +# end of cache maintain -- 2.33.0
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/28520 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/SED... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/28520 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/SED...
participants (2)
-
patchwork bot -
Yushan Wang