hulk inclusion category: feature bugzilla: 28055 CVE: NA
------------------------------
Renaming mpam_late_init() in arch/arm64/kernel/mpam.c to mpam_init(), traveling each MPAM ACPI cache / memory node and adding them to a list, with that, we use the numa node id it belongs to label cache node and proximity_domain to label memory node, once it ends, call mpam_init() to initialize all like before.
Code was partially borrowed from James's: http://www.linux-arm.org/git?p=linux-jm.git;a=commit;h=10fe7d6363ae96b 25f584d4a91f9d0f2fd5faf3b,"ACPI / MPAM: Parse the (draft) MPAM table [dead]"
v3->v5: mpam.c in drivers/acpi/arm64 should not be compiled when MPAM disabled, so we should add CONFIG_ACPI_MPAM macro and make CONFIG_MPAM select it. Not only that, as mpam init procedure is strong correlated to ACPI for Now (follow-up might be dependent on device tree), and CONFIG_ ACPI is not always selected under configuration, we should make CONFIG_ MPAM depends on CONFIG_ACPI before selecting CONFIG_ACPI_MPAM.
Signed-off-by: Wang ShaoBo bobo.shaobowang@huawei.com Reviewed-By: Xie XiuQi xiexiuqi@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- arch/arm64/Kconfig | 2 + arch/arm64/include/asm/mpam_resource.h | 15 +- arch/arm64/include/asm/mpam_sched.h | 8 +- arch/arm64/kernel/mpam.c | 246 ++++++++++++++++++------ drivers/acpi/arm64/Kconfig | 3 + drivers/acpi/arm64/Makefile | 1 + drivers/acpi/arm64/mpam.c | 249 +++++++++++++++++++++++++ 7 files changed, 468 insertions(+), 56 deletions(-) create mode 100644 drivers/acpi/arm64/mpam.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 269737b98763..95f8b6b39506 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -770,7 +770,9 @@ config ARM64_ERR_RECOV config MPAM bool "Support Memory Partitioning and Monitoring" default n + depends on ACPI select RESCTRL + select ACPI_MPAM if ACPI help Memory Partitioning and Monitoring. More exactly Memory system performance resource Partitioning and Monitoring diff --git a/arch/arm64/include/asm/mpam_resource.h b/arch/arm64/include/asm/mpam_resource.h index beadd2c64c31..ab90596c9194 100644 --- a/arch/arm64/include/asm/mpam_resource.h +++ b/arch/arm64/include/asm/mpam_resource.h @@ -95,6 +95,8 @@ */
struct mpam_node { + /* for label mpam_node instance*/ + u32 component_id; /* MPAM node header */ u8 type; /* MPAM_SMMU, MPAM_CACHE, MPAM_MC */ u64 addr; @@ -105,8 +107,19 @@ struct mpam_node { /* for debug */ char *cpus_list; char *name; + struct list_head list; };
-int mpam_nodes_init(void); +int __init mpam_force_init(void); + +int __init mpam_nodes_discovery_start(void); + +void __init mpam_nodes_discovery_failed(void); + +int __init mpam_nodes_discovery_complete(void); + +int mpam_create_cache_node(u32 component_id, phys_addr_t hwpage_address); + +int mpam_create_memory_node(u32 component_id, phys_addr_t hwpage_address);
#endif /* _ASM_ARM64_MPAM_RESOURCE_H */ diff --git a/arch/arm64/include/asm/mpam_sched.h b/arch/arm64/include/asm/mpam_sched.h index f0552e6dc9bc..350296157087 100644 --- a/arch/arm64/include/asm/mpam_sched.h +++ b/arch/arm64/include/asm/mpam_sched.h @@ -40,7 +40,13 @@ static inline void mpam_sched_in(void) __mpam_sched_in(); }
-extern int __read_mostly mpam_enabled; +enum mpam_enable_type { + enable_denied = 0, + enable_default, + enable_acpi, +}; + +extern enum mpam_enable_type __read_mostly mpam_enabled;
#else
diff --git a/arch/arm64/kernel/mpam.c b/arch/arm64/kernel/mpam.c index c728b1fe91dd..202e49a1d3f9 100644 --- a/arch/arm64/kernel/mpam.c +++ b/arch/arm64/kernel/mpam.c @@ -62,12 +62,6 @@ int max_name_width, max_data_width; */ bool rdt_alloc_capable;
-char *mpam_types_str[] = { - "MPAM_RESOURCE_SMMU", - "MPAM_RESOURCE_CACHE", - "MPAM_RESOURCE_MC", -}; - /* * Hi1620 2P Base Address Map * @@ -81,35 +75,30 @@ char *mpam_types_str[] = { * AFF2: MPIDR.AFF2 */
-#define MPAM_BASE(suffix, offset) ((suffix) << 24 | (offset) << 16) -#define MPAM_NODE(n, t, suffix, offset) \ - { \ - .name = #n, \ - .type = t, \ - .addr = MPAM_BASE(suffix, (offset)), \ - .cpus_list = "0", \ - } +static inline void mpam_node_assign_val(struct mpam_node *n, + char *name, + u8 type, + phys_addr_t hwpage_address, + u32 component_id) +{ + n->name = name; + n->type = type; + n->addr = hwpage_address; + n->component_id = component_id; + n->cpus_list = "0"; +}
-struct mpam_node mpam_node_all[] = { - MPAM_NODE(L3TALL0, MPAM_RESOURCE_CACHE, 0x000098ULL, 0xB9), - MPAM_NODE(L3TALL1, MPAM_RESOURCE_CACHE, 0x000090ULL, 0xB9), - MPAM_NODE(L3TALL2, MPAM_RESOURCE_CACHE, 0x200098ULL, 0xB9), - MPAM_NODE(L3TALL3, MPAM_RESOURCE_CACHE, 0x200090ULL, 0xB9), +#define MPAM_NODE_NAME_SIZE (10)
- MPAM_NODE(HHAALL0, MPAM_RESOURCE_MC, 0x000098ULL, 0xC1), - MPAM_NODE(HHAALL1, MPAM_RESOURCE_MC, 0x000090ULL, 0xC1), - MPAM_NODE(HHAALL2, MPAM_RESOURCE_MC, 0x200098ULL, 0xC1), - MPAM_NODE(HHAALL3, MPAM_RESOURCE_MC, 0x200090ULL, 0xC1), -}; +struct mpam_node *mpam_nodes_ptr;
-void mpam_nodes_unmap(void) +static int __init mpam_init(void); + +static void mpam_nodes_unmap(void) { - int i; - size_t num_nodes = ARRAY_SIZE(mpam_node_all); struct mpam_node *n;
- for (i = 0; i < num_nodes; i++) { - n = &mpam_node_all[i]; + list_for_each_entry(n, &mpam_nodes_ptr->list, list) { if (n->base) { iounmap(n->base); n->base = NULL; @@ -117,14 +106,12 @@ void mpam_nodes_unmap(void) } }
-int mpam_nodes_init(void) +static int mpam_nodes_init(void) { - int i, ret = 0; - size_t num_nodes = ARRAY_SIZE(mpam_node_all); + int ret = 0; struct mpam_node *n;
- for (i = 0; i < num_nodes; i++) { - n = &mpam_node_all[i]; + list_for_each_entry(n, &mpam_nodes_ptr->list, list) { ret |= cpulist_parse(n->cpus_list, &n->cpu_mask); n->base = ioremap(n->addr, 0x10000); if (!n->base) { @@ -136,6 +123,160 @@ int mpam_nodes_init(void) return ret; }
+static void mpam_nodes_destroy(void) +{ + struct mpam_node *n, *tmp; + + if (!mpam_nodes_ptr) + return; + + list_for_each_entry_safe(n, tmp, &mpam_nodes_ptr->list, list) { + kfree(n->name); + list_del(&n->list); + kfree(n); + } + + list_del(&mpam_nodes_ptr->list); + kfree(mpam_nodes_ptr); + mpam_nodes_ptr = NULL; +} + +int __init mpam_nodes_discovery_start(void) +{ + if (!mpam_enabled) + return -EINVAL; + + mpam_nodes_ptr = kzalloc(sizeof(struct mpam_node), GFP_KERNEL); + if (!mpam_nodes_ptr) + return -ENOMEM; + + INIT_LIST_HEAD(&mpam_nodes_ptr->list); + + return 0; +} + +void __init mpam_nodes_discovery_failed(void) +{ + mpam_nodes_destroy(); +} + +int __init mpam_nodes_discovery_complete(void) +{ + return mpam_init(); +} + +static inline int validate_mpam_node(int type, + int component_id) +{ + int ret = 0; + struct mpam_node *n; + + list_for_each_entry(n, &mpam_nodes_ptr->list, list) { + if (n->component_id == component_id && + n->type == type) { + ret = -EINVAL; + break; + } + } + + return ret; +} + +int mpam_create_cache_node(u32 component_id, + phys_addr_t hwpage_address) +{ + struct mpam_node *new; + char *name; + + if (validate_mpam_node(MPAM_RESOURCE_CACHE, component_id)) + goto skip; + + new = kzalloc(sizeof(struct mpam_node), GFP_KERNEL); + if (!new) + return -ENOMEM; + + name = kzalloc(MPAM_NODE_NAME_SIZE, GFP_KERNEL); + if (!name) { + kfree(new); + return -ENOMEM; + } + snprintf(name, MPAM_NODE_NAME_SIZE, "%s%d", "L3TALL", component_id); + + mpam_node_assign_val(new, + name, + MPAM_RESOURCE_CACHE, + hwpage_address, + component_id); + list_add_tail(&new->list, &mpam_nodes_ptr->list); + +skip: + return 0; +} + +int mpam_create_memory_node(u32 component_id, + phys_addr_t hwpage_address) +{ + struct mpam_node *new; + char *name; + + if (validate_mpam_node(MPAM_RESOURCE_MC, component_id)) + goto skip; + + new = kzalloc(sizeof(struct mpam_node), GFP_KERNEL); + if (!new) + return -ENOMEM; + + name = kzalloc(MPAM_NODE_NAME_SIZE, GFP_KERNEL); + if (!name) { + kfree(new); + return -ENOMEM; + } + snprintf(name, MPAM_NODE_NAME_SIZE, "%s%d", "HHAALL", component_id); + + mpam_node_assign_val(new, + name, + MPAM_RESOURCE_MC, + hwpage_address, + component_id); + list_add_tail(&new->list, &mpam_nodes_ptr->list); + +skip: + return 0; + +} + +int __init mpam_force_init(void) +{ + int ret; + + if (mpam_enabled != enable_default) + return 0; + + ret = mpam_nodes_discovery_start(); + if (ret) + return ret; + + ret |= mpam_create_cache_node(0, 0x000098b90000ULL); + ret |= mpam_create_cache_node(1, 0x000090b90000ULL); + ret |= mpam_create_cache_node(2, 0x200098b90000ULL); + ret |= mpam_create_cache_node(3, 0x200090b90000ULL); + ret |= mpam_create_memory_node(0, 0x000098c10000ULL); + ret |= mpam_create_memory_node(1, 0x000090c10000ULL); + ret |= mpam_create_memory_node(2, 0x200098c10000ULL); + ret |= mpam_create_memory_node(3, 0x200090c10000ULL); + if (ret) { + mpam_nodes_discovery_failed(); + pr_err("Failed to force create mpam node\n"); + return -EINVAL; + } + + ret = mpam_nodes_discovery_complete(); + if (!ret) + pr_info("Successfully init mpam by hardcode.\n"); + + return 1; +} + static void cat_wrmsr(struct rdt_domain *d, int partid); static void @@ -1137,16 +1278,14 @@ static void mpam_domains_destroy(struct resctrl_resource *r)
static void mpam_domains_init(struct resctrl_resource *r) { - int i, id = 0; - size_t num_nodes = ARRAY_SIZE(mpam_node_all); + int id = 0; struct mpam_node *n; struct list_head *add_pos = NULL; struct rdt_domain *d; struct raw_resctrl_resource *rr = (struct raw_resctrl_resource *)r->res; u32 val;
- for (i = 0; i < num_nodes; i++) { - n = &mpam_node_all[i]; + list_for_each_entry(n, &mpam_nodes_ptr->list, list) { if (r->rid != n->type) continue;
@@ -1215,25 +1354,22 @@ static void mpam_domains_init(struct resctrl_resource *r) } }
-int __read_mostly mpam_enabled; +enum mpam_enable_type __read_mostly mpam_enabled; static int __init mpam_setup(char *str) { - mpam_enabled = 1; + if (!strcmp(str, "=acpi")) + mpam_enabled = enable_acpi; + else + mpam_enabled = enable_default; return 1; } __setup("mpam", mpam_setup);
-static int __init mpam_late_init(void) +static int __init mpam_init(void) { struct resctrl_resource *r; int state, ret;
- if (!mpam_enabled) - return 0; - - if (!cpus_have_const_cap(ARM64_HAS_MPAM)) - return -ENODEV; - rdt_alloc_capable = 1; rdt_mon_capable = 1;
@@ -1242,7 +1378,7 @@ static int __init mpam_late_init(void) ret = mpam_nodes_init(); if (ret) { pr_err("internal error: bad cpu list\n"); - return ret; + goto out; }
mpam_domains_init(&resctrl_resources_all[MPAM_RESOURCE_CACHE]); @@ -1251,8 +1387,10 @@ static int __init mpam_late_init(void) state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "arm64/mpam:online:", mpam_online_cpu, mpam_offline_cpu); - if (state < 0) - return state; + if (state < 0) { + ret = state; + goto out; + }
register_resctrl_specific_files(res_specific_files, ARRAY_SIZE(res_specific_files));
@@ -1262,7 +1400,7 @@ static int __init mpam_late_init(void) ret = resctrl_group_init(); if (ret) { cpuhp_remove_state(state); - return ret; + goto out; }
for_each_resctrl_resource(r) { @@ -1275,11 +1413,11 @@ static int __init mpam_late_init(void) pr_info("MPAM %s monitoring detected\n", r->name); }
- return 0; +out: + mpam_nodes_destroy(); + return ret; }
-late_initcall(mpam_late_init); - /* * __intel_rdt_sched_in() - Writes the task's CLOSid/RMID to IA32_PQR_MSR * diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig index 5a6f80fce0d6..4ef04f0ea94c 100644 --- a/drivers/acpi/arm64/Kconfig +++ b/drivers/acpi/arm64/Kconfig @@ -7,3 +7,6 @@ config ACPI_IORT
config ACPI_GTDT bool + +config ACPI_MPAM + bool diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile index 1017def2ea12..81408ce40506 100644 --- a/drivers/acpi/arm64/Makefile +++ b/drivers/acpi/arm64/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_ACPI_IORT) += iort.o obj-$(CONFIG_ACPI_GTDT) += gtdt.o +obj-$(CONFIG_ACPI_MPAM) += mpam.o diff --git a/drivers/acpi/arm64/mpam.c b/drivers/acpi/arm64/mpam.c new file mode 100644 index 000000000000..1f82dce33e07 --- /dev/null +++ b/drivers/acpi/arm64/mpam.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Common code for ARM v8 MPAM ACPI + * + * Copyright (C) 2019-2020 Huawei Technologies Co., Ltd + * + * Author: Wang ShaoBo bobo.shaobowang@huawei.com + * + * Code was partially borrowed from http://www.linux-arm.org/git?p= + * linux-jm.git;a=commit;h=10fe7d6363ae96b25f584d4a91f9d0f2fd5faf3b. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +/* Parse the MPAM ACPI table feeding the discovered nodes into the driver */ +#define pr_fmt(fmt) "ACPI MPAM: " fmt + +#include <linux/acpi.h> +#include <acpi/processor.h> +#include <linux/cpu.h> +#include <linux/cpumask.h> +#include <linux/cacheinfo.h> +#include <linux/string.h> +#include <linux/nodemask.h> +#include <asm/mpam_resource.h> + +/** + * acpi_mpam_label_cache_component_id() - Recursivly find @min_physid + * for all leaf CPUs below @cpu_node, use numa node id of @min_cpu_node + * to label mpam cache node, which be signed by @component_id. + * @table_hdr: Pointer to the head of the PPTT table + * @cpu_node: The point in the toplogy to start the walk + * @component_id: The id labels the structure mpam_node cache + */ +static int +acpi_mpam_label_cache_component_id(struct acpi_table_header *table_hdr, + struct acpi_pptt_processor *cpu_node, + u32 *component_id) +{ + phys_cpuid_t min_physid = PHYS_CPUID_INVALID; + struct acpi_pptt_processor *min_cpu_node = NULL; + u32 logical_cpuid; + u32 acpi_processor_id; + + acpi_pptt_find_min_physid_cpu_node(table_hdr, + cpu_node, + &min_physid, + &min_cpu_node); + WARN_ON_ONCE(invalid_phys_cpuid(min_physid)); + if (min_cpu_node == NULL) + return -EINVAL; + + acpi_processor_id = min_cpu_node->acpi_processor_id; + logical_cpuid = acpi_map_cpuid(min_physid, acpi_processor_id); + if (invalid_logical_cpuid(logical_cpuid) || + !cpu_present(logical_cpuid)) { + pr_err_once("Invalid logical cpuid.\n"); + return -EINVAL; + } + + *component_id = cpu_to_node(logical_cpuid); + + return 0; +} + +/** + * acpi_mpam_label_memory_component_id() - Use proximity_domain id to + * label mpam memory node, which be signed by @component_id. + * @proximity_domain: proximity_domain of ACPI MPAM memory node + * @component_id: The id labels the structure mpam_node memory + */ +static int acpi_mpam_label_memory_component_id(u8 proximity_domain, + u32 *component_id) +{ + u32 nid = (u32)proximity_domain; + + if (nid >= nr_online_nodes) { + pr_err_once("Invalid proximity domain\n"); + return -EINVAL; + } + + *component_id = nid; + return 0; +} + +static int __init acpi_mpam_parse_memory(struct acpi_mpam_header *h) +{ + int ret = 0; + u32 component_id; + struct acpi_mpam_node_memory *node = (struct acpi_mpam_node_memory *)h; + + ret = acpi_mpam_label_memory_component_id(node->proximity_domain, + &component_id); + if (ret) { + pr_err("Failed to label memory component id\n"); + return -EINVAL; + } + + ret = mpam_create_memory_node(component_id, + node->header.base_address); + if (ret) { + pr_err("Failed to create memory node\n"); + return -EINVAL; + } + + return ret; +} + +static int __init acpi_mpam_parse_cache(struct acpi_mpam_header *h, + struct acpi_table_header *pptt) +{ + int ret = 0; + u32 component_id; + struct acpi_pptt_cache *pptt_cache; + struct acpi_pptt_processor *pptt_cpu_node; + struct acpi_mpam_node_cache *node = (struct acpi_mpam_node_cache *)h; + + if (!pptt) { + pr_err("No PPTT table found, MPAM cannot be configured\n"); + return -EINVAL; + } + + pptt_cache = acpi_pptt_validate_cache_node(pptt, node->PPTT_ref); + if (!pptt_cache) { + pr_err("Broken PPTT reference in the MPAM table\n"); + return -EINVAL; + } + + /* + * We actually need a cpu_node, as a pointer to the PPTT cache + * description isn't unique. + */ + pptt_cpu_node = acpi_pptt_find_cache_backwards(pptt, pptt_cache); + + ret = acpi_mpam_label_cache_component_id(pptt, pptt_cpu_node, + &component_id); + + if (ret) { + pr_err("Failed to label cache component id\n"); + return -EINVAL; + } + + ret = mpam_create_cache_node(component_id, + node->header.base_address); + if (ret) { + pr_err("Failed to create cache node\n"); + return -EINVAL; + } + + return ret; +} + +static int __init acpi_mpam_parse_table(struct acpi_table_header *table, + struct acpi_table_header *pptt) +{ + char *table_offset = (char *)(table + 1); + char *table_end = (char *)table + table->length; + struct acpi_mpam_header *node_hdr; + int ret = 0; + + ret = mpam_nodes_discovery_start(); + if (ret) + return ret; + + node_hdr = (struct acpi_mpam_header *)table_offset; + while (table_offset < table_end) { + switch (node_hdr->type) { + + case ACPI_MPAM_TYPE_CACHE: + ret = acpi_mpam_parse_cache(node_hdr, pptt); + break; + case ACPI_MPAM_TYPE_MEMORY: + ret = acpi_mpam_parse_memory(node_hdr); + break; + default: + pr_warn_once("Unknown node type %u offset %ld.", + node_hdr->type, + (table_offset-(char *)table)); + /* fall through */ + case ACPI_MPAM_TYPE_SMMU: + /* not yet supported */ + /* fall through */ + case ACPI_MPAM_TYPE_UNKNOWN: + break; + } + if (ret) + break; + + table_offset += node_hdr->length; + node_hdr = (struct acpi_mpam_header *)table_offset; + } + + if (ret) { + pr_err("discovery failed: %d\n", ret); + mpam_nodes_discovery_failed(); + } else { + ret = mpam_nodes_discovery_complete(); + if (!ret) + pr_info("Successfully init mpam by ACPI.\n"); + } + + return ret; +} + +int __init acpi_mpam_parse(void) +{ + struct acpi_table_header *mpam, *pptt; + acpi_status status; + int ret; + + if (!cpus_have_const_cap(ARM64_HAS_MPAM)) + return 0; + + ret = mpam_force_init(); + if (ret) + return 0; + + if (acpi_disabled) + return 0; + + status = acpi_get_table(ACPI_SIG_MPAM, 0, &mpam); + if (ACPI_FAILURE(status)) + return -ENOENT; + + /* PPTT is optional, there may be no mpam cache controls */ + acpi_get_table(ACPI_SIG_PPTT, 0, &pptt); + if (ACPI_FAILURE(status)) + pptt = NULL; + + ret = acpi_mpam_parse_table(mpam, pptt); + acpi_put_table(pptt); + acpi_put_table(mpam); + + return ret; +} + +/* + * We want to run after cacheinfo_sysfs_init() has caused the cacheinfo + * structures to be populated. That runs as a device_initcall. + */ +device_initcall_sync(acpi_mpam_parse);