Hi Jonathan, On Tue, Aug 08, 2023 at 12:57:09PM +0100, Jonathan Cameron via wrote: > Date: Tue, 8 Aug 2023 12:57:09 +0100 > From: Jonathan Cameron via <qemu-devel@nongnu.org> > Subject: [RFC PATCH 1/5] hw/acpi: Add PPTT cache descriptions > X-Mailer: git-send-email 2.39.2 > > Current PPTT tables generated by QEMU only provide information on CPU > topology and neglect the description of Caches. > > This patch adds flexible definition of those caches and updates the > table version to 3 to allow for the per CPU cache instance IDs needed > for cross references from the MPAM table. > > If MPAM is not being used, then a unified description can be used, > greatly reducing the resulting table size. > > New machine parameters are used to control the cache toplogy. > cache-cluster-start-level: Which caches are associated with the cluster > level of the topology. e.g cache-cluster-start-level=2 results in shared > l2 cache across a cluster.
So the i/d cache are at core level by default and we don't need to configure its topology, right? > cache-numa-start-level: Which caches are associate with the NUMA (in qemu > this is currently the physical package level). I'm a bit confused about the connection of this numa and l3. Does there "NUMA" refer to socket level? > For example > cache-cluster-start-level=2,cache-numa-start-level=3 gives > private l1, cluster shared l2 and package shared L3. Okay, you list the topology as: l1 per core, l2 per cluster and l3 per socket. For this case, I think my QOM topology proposal [1] (this is the underlying general topology implementation, compatible with symmetric and heterogeneous, and I'm working on this QOM topology as a superset of smp) is compatible with your command. And I understand the difference between my "x-l2-cache-topo=[core|cluster]" for x86 and yours is that I named the l2 cache, while you took level count as the parameter. What if I extend my symmetric cache topology commands for i386 as "l2-cache=cluster,l3-cache=socket (*)"? Compared to cache-cluster-start-level=2,cache-numa-start-level=3, are there some specific cases that cache-xxx-start-level can solves but (*) command cannot? [1]: https://mail.gnu.org/archive/html/qemu-devel/2023-02/msg05167.html > > FIXME: Test updates. > > Signed-off-by: Jonathan Cameron <jonathan.came...@huawei.com> > --- > qapi/machine.json | 8 +- > include/hw/acpi/aml-build.h | 19 +++- > include/hw/boards.h | 4 + > hw/acpi/aml-build.c | 189 ++++++++++++++++++++++++++++++++++-- > hw/arm/virt-acpi-build.c | 130 ++++++++++++++++++++++++- > hw/core/machine-smp.c | 8 ++ > hw/loongarch/acpi-build.c | 2 +- > 7 files changed, 350 insertions(+), 10 deletions(-) > > diff --git a/qapi/machine.json b/qapi/machine.json > index a08b6576ca..cc86784641 100644 > --- a/qapi/machine.json > +++ b/qapi/machine.json > @@ -1494,6 +1494,10 @@ > # @maxcpus: maximum number of hotpluggable virtual CPUs in the virtual > # machine > # > +# @cache-cluster-start-level: Level of first cache attached to cluster > +# > +# @cache-node-start-level: Level of first cache attached to cluster node or numa? Thanks, Zhao > +# > # Since: 6.1 > ## > { 'struct': 'SMPConfiguration', 'data': { > @@ -1503,7 +1507,9 @@ > '*clusters': 'int', > '*cores': 'int', > '*threads': 'int', > - '*maxcpus': 'int' } } > + '*maxcpus': 'int', > + '*cache-cluster-start-level': 'int', > + '*cache-node-start-level': 'int'} } > > ## > # @x-query-irq: > diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h > index d1fb08514b..055b74820d 100644 > --- a/include/hw/acpi/aml-build.h > +++ b/include/hw/acpi/aml-build.h > @@ -489,8 +489,25 @@ void build_srat_memory(GArray *table_data, uint64_t base, > void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms, > const char *oem_id, const char *oem_table_id); > > +typedef enum ACPIPPTTCacheType { > + DATA, > + INSTRUCTION, > + UNIFIED, > +} ACPIPPTTCacheType; > + > +typedef struct ACPIPPTTCache { > + ACPIPPTTCacheType type; > + int sets; > + int size; > + int associativity; > + int linesize; > + unsigned int pptt_id; > + int level; > +} ACPIPPTTCache; > + > void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, > - const char *oem_id, const char *oem_table_id); > + const char *oem_id, const char *oem_table_id, > + int num_caches, ACPIPPTTCache *caches); > > void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, > const char *oem_id, const char *oem_table_id); > diff --git a/include/hw/boards.h b/include/hw/boards.h > index ed83360198..6e8ab92684 100644 > --- a/include/hw/boards.h > +++ b/include/hw/boards.h > @@ -316,6 +316,8 @@ typedef struct DeviceMemoryState { > * @cores: the number of cores in one cluster > * @threads: the number of threads in one core > * @max_cpus: the maximum number of logical processors on the machine > + * @cache_cluster_start_level: First cache level attached to cluster > + * @cache_node_start_level: First cache level attached to node > */ > typedef struct CpuTopology { > unsigned int cpus; > @@ -325,6 +327,8 @@ typedef struct CpuTopology { > unsigned int cores; > unsigned int threads; > unsigned int max_cpus; > + unsigned int cache_cluster_start_level; > + unsigned int cache_node_start_level; > } CpuTopology; > > /** > diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c > index ea331a20d1..e103cd638f 100644 > --- a/hw/acpi/aml-build.c > +++ b/hw/acpi/aml-build.c > @@ -1994,32 +1994,175 @@ static void build_processor_hierarchy_node(GArray > *tbl, uint32_t flags, > } > } > > +static void build_cache_nodes(GArray *tbl, ACPIPPTTCache *cache, > + uint32_t next_offset, > + bool has_id, unsigned int id) > +{ > + int val; > + > + /* Type 1 - cache */ > + build_append_byte(tbl, 1); > + /* Length */ > + build_append_byte(tbl, 28); > + /* Reserved */ > + build_append_int_noprefix(tbl, 0, 2); > + /* Flags - everything except possibly the ID */ > + build_append_int_noprefix(tbl, has_id ? 0xff : 0x7f, 4); > + /* Offset of next cache up */ > + build_append_int_noprefix(tbl, next_offset, 4); > + build_append_int_noprefix(tbl, cache->size, 4); > + build_append_int_noprefix(tbl, cache->sets, 4); > + build_append_byte(tbl, cache->associativity); > + /* Read and Write allocate amd WB */ > + val = 0x3 | (1 << 4); > + switch (cache->type) { > + case INSTRUCTION: > + val |= (1 << 2); > + break; > + case DATA: > + val |= (0 << 2); /* Data */ > + break; > + case UNIFIED: > + val |= (3 << 2); /* Unified */ > + break; > + } > + build_append_byte(tbl, val); > + build_append_int_noprefix(tbl, cache->linesize, 2); > + build_append_int_noprefix(tbl, > + has_id ? > + (cache->type << 24) | (cache->level << 16) | > id : > + 0, 4); > +} > + > +static void build_caches_subset(GArray *table_data, uint32_t pptt_start, > + int num_caches, ACPIPPTTCache *caches, > + bool assign_ids, int base_id, > + uint8_t level_high, uint8_t level_low, > + uint32_t *data_offset, uint32_t > *instr_offset) > +{ > + uint32_t next_level_offset_data = 0, next_level_offset_instruction = 0; > + uint32_t this_offset, next_offset = 0; > + int c, l; > + > + /* Walk caches from top to bottom */ > + > + for (l = level_high; l >= level_low; l--) { /* Walk down levels */ > + for (c = 0; c < num_caches; c++) { > + if (caches[c].level != l) { > + continue; > + } > + > + /* Assume only unified above l1 for now */ > + this_offset = table_data->len - pptt_start; > + switch (caches[c].type) { > + case INSTRUCTION: > + next_offset = next_level_offset_instruction; > + break; > + case DATA: > + next_offset = next_level_offset_data; > + break; > + case UNIFIED: > + /* Either is fine here - hopefully */ > + next_offset = next_level_offset_instruction; > + break; > + } > + build_cache_nodes(table_data, &caches[c], next_offset, > + assign_ids, base_id); > + switch (caches[c].type) { > + case INSTRUCTION: > + next_level_offset_instruction = this_offset; > + break; > + case DATA: > + next_level_offset_data = this_offset; > + break; > + case UNIFIED: > + next_level_offset_instruction = this_offset; > + next_level_offset_data = this_offset; > + break; > + } > + *data_offset = next_level_offset_data; > + *instr_offset = next_level_offset_instruction; > + } > + } > +} > + > /* > * ACPI spec, Revision 6.3 > * 5.2.29 Processor Properties Topology Table (PPTT) > */ > void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, > - const char *oem_id, const char *oem_table_id) > + const char *oem_id, const char *oem_table_id, > + int num_caches, ACPIPPTTCache *caches) > { > + bool share_structs = false; > MachineClass *mc = MACHINE_GET_CLASS(ms); > CPUArchIdList *cpus = ms->possible_cpus; > int64_t socket_id = -1, cluster_id = -1, core_id = -1; > uint32_t socket_offset = 0, cluster_offset = 0, core_offset = 0; > uint32_t pptt_start = table_data->len; > int n; > - AcpiTable table = { .sig = "PPTT", .rev = 2, > + AcpiTable table = { .sig = "PPTT", .rev = 3, > .oem_id = oem_id, .oem_table_id = oem_table_id }; > + uint32_t l1_data_offset = 0; > + uint32_t l1_instr_offset = 0; > + uint32_t cluster_data_offset = 0; > + uint32_t cluster_instr_offset = 0; > + uint32_t node_data_offset = 0; > + uint32_t node_instr_offset = 0; > + int top_node = 7; > + int top_cluster = 7; > + int top_core = 7; > > acpi_table_begin(&table, table_data); > > + /* Let us have a unified cache description for now */ > + > + if (share_structs && num_caches >= 1) { > + if (ms->smp.cache_node_start_level) { > + build_caches_subset(table_data, pptt_start, num_caches, caches, > + false, 0, > + top_node, ms->smp.cache_node_start_level, > + &node_data_offset, &node_instr_offset); > + top_cluster = ms->smp.cache_node_start_level - 1; > + } > + /* Assumption that some caches below this */ > + if (ms->smp.cache_cluster_start_level) { > + build_caches_subset(table_data, pptt_start, num_caches, caches, > + false, 0, > + top_cluster, > ms->smp.cache_cluster_start_level, > + &cluster_data_offset, &cluster_instr_offset); > + top_core = ms->smp.cache_cluster_start_level - 1; > + } > + build_caches_subset(table_data, pptt_start, num_caches, caches, > + false, 0, > + top_core , 0, > + &l1_data_offset, &l1_instr_offset); > + } > + > /* > * This works with the assumption that cpus[n].props.*_id has been > * sorted from top to down levels in mc->possible_cpu_arch_ids(). > * Otherwise, the unexpected and duplicated containers will be > * created. > */ > + > for (n = 0; n < cpus->len; n++) { > if (cpus->cpus[n].props.socket_id != socket_id) { > + uint32_t priv_rsrc[2]; > + int num_priv = 0; > + > + if (!share_structs && ms->smp.cache_node_start_level) { > + build_caches_subset(table_data, pptt_start, num_caches, > caches, > + true, n, > + top_node, ms->smp.cache_node_start_level, > + &node_data_offset, &node_instr_offset); > + top_cluster = ms->smp.cache_node_start_level - 1; > + } > + priv_rsrc[0] = node_instr_offset; > + priv_rsrc[1] = node_data_offset; > + if (node_instr_offset || node_data_offset) { > + num_priv = node_instr_offset == node_data_offset ? 1 : 2; > + } > assert(cpus->cpus[n].props.socket_id > socket_id); > socket_id = cpus->cpus[n].props.socket_id; > cluster_id = -1; > @@ -2027,36 +2170,70 @@ void build_pptt(GArray *table_data, BIOSLinker > *linker, MachineState *ms, > socket_offset = table_data->len - pptt_start; > build_processor_hierarchy_node(table_data, > (1 << 0), /* Physical package */ > - 0, socket_id, NULL, 0); > + 0, socket_id, priv_rsrc, num_priv); > } > > + > if (mc->smp_props.clusters_supported && mc->smp_props.has_clusters) { > if (cpus->cpus[n].props.cluster_id != cluster_id) { > + uint32_t priv_rsrc[2]; > + int num_priv = 0; > + > + if (!share_structs && ms->smp.cache_cluster_start_level) { > + build_caches_subset(table_data, pptt_start, num_caches, > + caches, true, n, > + top_cluster, > + ms->smp.cache_cluster_start_level, > + &cluster_data_offset, > + &cluster_instr_offset); > + top_core = ms->smp.cache_cluster_start_level - 1; > + } > + priv_rsrc[0] = cluster_instr_offset; > + priv_rsrc[1] = cluster_data_offset; > + > assert(cpus->cpus[n].props.cluster_id > cluster_id); > cluster_id = cpus->cpus[n].props.cluster_id; > core_id = -1; > cluster_offset = table_data->len - pptt_start; > + > + if (cluster_instr_offset || cluster_data_offset) { > + num_priv = cluster_instr_offset == cluster_data_offset ? > + 1 : 2; > + } > build_processor_hierarchy_node(table_data, > (0 << 0), /* Not a physical package */ > - socket_offset, cluster_id, NULL, 0); > + socket_offset, cluster_id, priv_rsrc, num_priv); > } > } else { > cluster_offset = socket_offset; > } > > + if (!share_structs && > + cpus->cpus[n].props.core_id != core_id) { > + build_caches_subset(table_data, pptt_start, num_caches, caches, > + true, n, > + top_core , 0, > + &l1_data_offset, &l1_instr_offset); > + } > if (ms->smp.threads == 1) { > + uint32_t priv_rsrc[2] = { l1_instr_offset, l1_data_offset }; > + > build_processor_hierarchy_node(table_data, > (1 << 1) | /* ACPI Processor ID valid */ > (1 << 3), /* Node is a Leaf */ > - cluster_offset, n, NULL, 0); > + cluster_offset, n, priv_rsrc, > + l1_instr_offset == l1_data_offset ? 1 : 2); > } else { > if (cpus->cpus[n].props.core_id != core_id) { > + uint32_t priv_rsrc[2] = { l1_instr_offset, l1_data_offset }; > + > assert(cpus->cpus[n].props.core_id > core_id); > core_id = cpus->cpus[n].props.core_id; > core_offset = table_data->len - pptt_start; > build_processor_hierarchy_node(table_data, > (0 << 0), /* Not a physical package */ > - cluster_offset, core_id, NULL, 0); > + cluster_offset, core_id, priv_rsrc, > + l1_instr_offset == l1_data_offset ? 1 : 2); > } > > build_processor_hierarchy_node(table_data, > diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c > index 6b674231c2..ec8fdcefff 100644 > --- a/hw/arm/virt-acpi-build.c > +++ b/hw/arm/virt-acpi-build.c > @@ -922,6 +922,129 @@ static void acpi_align_size(GArray *blob, unsigned > align) > g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); > } > > +static unsigned int virt_get_caches(VirtMachineState *vms, > + ACPIPPTTCache *caches) > +{ > + ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0)); > + bool ccidx = cpu_isar_feature(any_ccidx, armcpu); > + unsigned int num_cache, i; > + int level_instr = 1, level_data = 1; > + > + for (i = 0, num_cache = 0; i < 7; i++, num_cache++) { > + int type = (armcpu->clidr >> (3 * i)) & 7; > + int bank_index; > + int level = 0; > + ACPIPPTTCacheType cache_type = INSTRUCTION; > + > + if (type == 0) { > + break; > + } > + > + switch (type) { > + case 1: > + cache_type = INSTRUCTION; > + level = level_instr; > + break; > + case 2: > + cache_type = DATA; > + level = level_data; > + break; > + case 4: > + cache_type = UNIFIED; > + level = level_instr > level_data ? level_instr : level_data; > + break; > + case 3: /* Split - Do data first */ > + cache_type = DATA; > + level = level_data; > + break; > + } > + /* > + * ccsidr is indexed using both the level and whether it is > + * an instruction cache. Unified caches use the same storage > + * as data caches. > + */ > + bank_index = (i * 2) | ((type == 1) ? 1 : 0); > + if (ccidx) { > + caches[num_cache] = (ACPIPPTTCache) { > + .type = cache_type, > + .level = level, > + .linesize = 1 << (FIELD_EX64(armcpu->ccsidr[bank_index], > + CCSIDR_EL1, > + CCIDX_LINESIZE) + 4), > + .associativity = FIELD_EX64(armcpu->ccsidr[bank_index], > + CCSIDR_EL1, > + CCIDX_ASSOCIATIVITY) + 1, > + .sets = FIELD_EX64(armcpu->ccsidr[bank_index], CCSIDR_EL1, > + CCIDX_NUMSETS) + 1, > + }; > + } else { > + caches[num_cache] = (ACPIPPTTCache) { > + .type = cache_type, > + .level = level, > + .linesize = 1 << (FIELD_EX64(armcpu->ccsidr[bank_index], > + CCSIDR_EL1, LINESIZE) + 4), > + .associativity = FIELD_EX64(armcpu->ccsidr[bank_index], > + CCSIDR_EL1, > + ASSOCIATIVITY) + 1, > + .sets = FIELD_EX64(armcpu->ccsidr[bank_index], CCSIDR_EL1, > + NUMSETS) + 1, > + }; > + } > + caches[num_cache].size = caches[num_cache].associativity * > + caches[num_cache].sets * caches[num_cache].linesize; > + > + /* Break one 'split' entry up into two records */ > + if (type == 3) { > + num_cache++; > + bank_index = (i * 2) | 1; > + if (ccidx) { > + /* Instruction cache: bottom bit set when reading banked reg > */ > + caches[num_cache] = (ACPIPPTTCache) { > + .type = INSTRUCTION, > + .level = level_instr, > + .linesize = 1 << (FIELD_EX64(armcpu->ccsidr[bank_index], > + CCSIDR_EL1, > + CCIDX_LINESIZE) + 4), > + .associativity = FIELD_EX64(armcpu->ccsidr[bank_index], > + CCSIDR_EL1, > + CCIDX_ASSOCIATIVITY) + 1, > + .sets = FIELD_EX64(armcpu->ccsidr[bank_index], > CCSIDR_EL1, > + CCIDX_NUMSETS) + 1, > + }; > + } else { > + caches[num_cache] = (ACPIPPTTCache) { > + .type = INSTRUCTION, > + .level = level_instr, > + .linesize = 1 << (FIELD_EX64(armcpu->ccsidr[bank_index], > + CCSIDR_EL1, LINESIZE) + 4), > + .associativity = FIELD_EX64(armcpu->ccsidr[bank_index], > + CCSIDR_EL1, > + ASSOCIATIVITY) + 1, > + .sets = FIELD_EX64(armcpu->ccsidr[bank_index], > CCSIDR_EL1, > + NUMSETS) + 1, > + }; > + } > + caches[num_cache].size = caches[num_cache].associativity * > + caches[num_cache].sets * caches[num_cache].linesize; > + } > + switch (type) { > + case 1: > + level_instr++; > + break; > + case 2: > + level_data++; > + break; > + case 3: > + case 4: > + level_instr++; > + level_data++; > + break; > + } > + } > + > + return num_cache; > +} > + > static > void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) > { > @@ -930,6 +1053,8 @@ void virt_acpi_build(VirtMachineState *vms, > AcpiBuildTables *tables) > unsigned dsdt, xsdt; > GArray *tables_blob = tables->table_data; > MachineState *ms = MACHINE(vms); > + ACPIPPTTCache caches[16]; /* Can select up to 16 */ > + unsigned int num_cache; > > table_offsets = g_array_new(false, true /* clear */, > sizeof(uint32_t)); > @@ -949,10 +1074,13 @@ void virt_acpi_build(VirtMachineState *vms, > AcpiBuildTables *tables) > acpi_add_table(table_offsets, tables_blob); > build_madt(tables_blob, tables->linker, vms); > > + num_cache = virt_get_caches(vms, caches); > + > if (!vmc->no_cpu_topology) { > acpi_add_table(table_offsets, tables_blob); > build_pptt(tables_blob, tables->linker, ms, > - vms->oem_id, vms->oem_table_id); > + vms->oem_id, vms->oem_table_id, > + num_cache, caches); > } > > acpi_add_table(table_offsets, tables_blob); > diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c > index 0f4d9b6f7a..cbb0bf1bc7 100644 > --- a/hw/core/machine-smp.c > +++ b/hw/core/machine-smp.c > @@ -81,6 +81,10 @@ void machine_parse_smp_config(MachineState *ms, > unsigned cores = config->has_cores ? config->cores : 0; > unsigned threads = config->has_threads ? config->threads : 0; > unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0; > + unsigned cache_cl_start = config->has_cache_cluster_start_level ? > + config->cache_cluster_start_level : 0; > + unsigned cache_nd_start = config->has_cache_node_start_level ? > + config->cache_node_start_level : 0; > > /* > * Specified CPU topology parameters must be greater than zero, > @@ -161,6 +165,10 @@ void machine_parse_smp_config(MachineState *ms, > ms->smp.max_cpus = maxcpus; > > mc->smp_props.has_clusters = config->has_clusters; > + if (mc->smp_props.has_clusters) { > + ms->smp.cache_cluster_start_level = cache_cl_start; > + ms->smp.cache_node_start_level = cache_nd_start; > + } > > /* sanity-check of the computed topology */ > if (sockets * dies * clusters * cores * threads != maxcpus) { > diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c > index 0b62c3a2f7..51d4ed9a19 100644 > --- a/hw/loongarch/acpi-build.c > +++ b/hw/loongarch/acpi-build.c > @@ -439,7 +439,7 @@ static void acpi_build(AcpiBuildTables *tables, > MachineState *machine) > > acpi_add_table(table_offsets, tables_blob); > build_pptt(tables_blob, tables->linker, machine, > - lams->oem_id, lams->oem_table_id); > + lams->oem_id, lams->oem_table_id, 0, NULL); > > acpi_add_table(table_offsets, tables_blob); > build_srat(tables_blob, tables->linker, machine); > -- > 2.39.2 > >