From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:05
ACPI 6.2 adds the Processor Properties Topology Table (PPTT), which is
used to describe the processor and cache topologies. Ideally it is
used to extend/override information provided by the hardware, but
right now ARM64 is entirely dependent on firmware provided tables.
This patch parses the table for the cache topology and CPU topology.
For the latter we also add an additional topology_cod_id() macro,
and a package_id for arm64. Initially the physical id will match
the cluster id, but we update users of the cluster to utilize
the new macro. When we enable PPTT for the arm64 the cluster/socket
starts to differ. Because of this we also make some dynamic decisions
about mapping thread/core/cod/socket to the thread/socket used by the
scheduler.
For example on juno:
[root at mammon-juno-rh topology]# lstopo-no-graphics
Machine (7048MB)
Package L#0
L2 L#0 (1024KB) + Core L#0
L1d L#0 (32KB) + L1i L#0 (32KB) + PU L#0 (P#0)
L1d L#1 (32KB) + L1i L#1 (32KB) + PU L#1 (P#1)
L1d L#2 (32KB) + L1i L#2 (32KB) + PU L#2 (P#2)
L1d L#3 (32KB) + L1i L#3 (32KB) + PU L#3 (P#3)
L2 L#1 (2048KB) + Core L#1
L1d L#4 (32KB) + L1i L#4 (48KB) + PU L#4 (P#4)
L1d L#5 (32KB) + L1i L#5 (48KB) + PU L#5 (P#5)
HostBridge L#0
PCIBridge
PCIBridge
PCIBridge
PCI 1095:3132
Block(Disk) L#0 "sda"
PCIBridge
PCI 1002:68f9
GPU L#1 "renderD128"
GPU L#2 "card0"
GPU L#3 "controlD64"
PCIBridge
PCI 11ab:4380
Net L#4 "enp8s0"
Jeremy Linton (6):
ACPI/PPTT: Add Processor Properties Topology Table parsing
ACPI: Enable PPTT support on ARM64
drivers: base: cacheinfo: arm64: Add support for ACPI based firmware
tables
Topology: Add cluster on die macros and arm64 decoding
arm64: Fixup users of topology_physical_package_id
arm64: topology: Enable ACPI/PPTT based CPU topology.
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/topology.h | 4 +-
arch/arm64/kernel/cacheinfo.c | 23 +-
arch/arm64/kernel/topology.c | 76 +++++-
drivers/acpi/Makefile | 1 +
drivers/acpi/arm64/Kconfig | 3 +
drivers/acpi/pptt.c | 508 ++++++++++++++++++++++++++++++++++++++
drivers/base/cacheinfo.c | 17 +-
drivers/clk/clk-mb86s7x.c | 2 +-
drivers/cpufreq/arm_big_little.c | 2 +-
drivers/firmware/psci_checker.c | 2 +-
include/linux/cacheinfo.h | 10 +-
include/linux/topology.h | 5 +
13 files changed, 634 insertions(+), 20 deletions(-)
create mode 100644 drivers/acpi/pptt.c
--
2.13.5
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:06
ACPI 6.2 adds a new table, which describes how processing units
are related to each other in tree like fashion. Caches are
also sprinkled throughout the tree and describe the properties
of the caches in relation to other caches and processing units.
Add the code to parse the cache hierarchy and report the total
number of levels of cache for a given core using
acpi_find_last_cache_level() as well as fill out the individual
cores cache information with cache_setup_acpi() once the
cpu_cacheinfo structure has been populated by the arch specific
code.
Further, report peers in the topology using setup_acpi_cpu_topology()
to report a unique ID for each processing unit at a given level
in the tree. These unique id's can then be used to match related
processing units which exist as threads, COD (clusters
on die), within a given package, etc.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/acpi/pptt.c | 507 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 507 insertions(+)
create mode 100644 drivers/acpi/pptt.c
@@ -0,0 +1,507 @@+/*+*Copyright(C)2017,ARM+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsandconditionsoftheGNUGeneralPublicLicense,+*version2,aspublishedbytheFreeSoftwareFoundation.+*+*Thisprogramisdistributedinthehopeitwillbeuseful,butWITHOUT+*ANYWARRANTY;withouteventheimpliedwarrantyofMERCHANTABILITYor+*FITNESSFORAPARTICULARPURPOSE.SeetheGNUGeneralPublicLicensefor+*moredetails.+*+*ThisfileimplementsparsingofProcessorPropertiesTopologyTable(PPTT)+*whichisoptionallyusedtodescribetheprocessorandcachetopology.+*Duetotherelativepointersusedthroughoutthetable,thisdoesn't+*leveragetheexistingsubtableparsinginthekernel.+*/++#define pr_fmt(fmt) "ACPI PPTT: " fmt++#include<linux/acpi.h>+#include<linux/cacheinfo.h>+#include<acpi/processor.h>++/*+*GiventhePPTTtable,findandverifythatthesubtableentry+*islocatedwithinthetable+*/+staticstructacpi_subtable_header*fetch_pptt_subtable(+structacpi_table_header*table_hdr,u32pptt_ref)+{+structacpi_subtable_header*entry;++/* there isn't a subtable at reference 0 */+if(!pptt_ref)+returnNULL;++if(pptt_ref+sizeof(structacpi_subtable_header)>table_hdr->length)+returnNULL;++entry=(structacpi_subtable_header*)((u8*)table_hdr+pptt_ref);++if(pptt_ref+entry->length>table_hdr->length)+returnNULL;++returnentry;+}++staticstructacpi_pptt_processor*fetch_pptt_node(+structacpi_table_header*table_hdr,u32pptt_ref)+{+return(structacpi_pptt_processor*)fetch_pptt_subtable(table_hdr,pptt_ref);+}++staticstructacpi_pptt_cache*fetch_pptt_cache(+structacpi_table_header*table_hdr,u32pptt_ref)+{+return(structacpi_pptt_cache*)fetch_pptt_subtable(table_hdr,pptt_ref);+}++staticstructacpi_subtable_header*acpi_get_pptt_resource(+structacpi_table_header*table_hdr,+structacpi_pptt_processor*node,intresource)+{+u32ref;++if(resource>=node->number_of_priv_resources)+returnNULL;++ref=*(u32*)((u8*)node+sizeof(structacpi_pptt_processor)++sizeof(u32)*resource);++returnfetch_pptt_subtable(table_hdr,ref);+}++/*+*givenappttresource,verifythatitisacachenode,thenwalk+*downeachlevelofcaches,countinghowmanylevelsarefound+*aswellascheckingthecachetype(icache,dcache,unified).Ifa+*level&typematch,thenwesetfound,andcontinuethesearch.+*Oncetheentirecachebranchhasbeenwalkedreturnitsmax+*depth.+*/+staticintacpi_pptt_walk_cache(structacpi_table_header*table_hdr,+intlocal_level,+structacpi_subtable_header*res,+structacpi_pptt_cache**found,+intlevel,inttype)+{+structacpi_pptt_cache*cache;++if(res->type!=ACPI_PPTT_TYPE_CACHE)+return0;++cache=(structacpi_pptt_cache*)res;+while(cache){+local_level++;++if((local_level==level)&&+(cache->flags&ACPI_PPTT_CACHE_TYPE_VALID)&&+((cache->attributes&ACPI_PPTT_MASK_CACHE_TYPE)==type)){+if(*found!=NULL)+pr_err("Found duplicate cache level/type unable to determine uniqueness\n");++pr_debug("Found cache @ level %d\n",level);+*found=cache;+/*+*continuelookingatthisnode'sresourcelist+*toverifythatwedon'tfindaduplicate+*cachenode.+*/+}+cache=fetch_pptt_cache(table_hdr,cache->next_level_of_cache);+}+returnlocal_level;+}++/*+*GivenaCPUnodelookforcachelevelsthatexistatthislevel,andthen+*foreachcachenode,counthowmanylevelsexistbelow(logicallyabove)it.+*Ifalevelandtypearespecified,andwefindthatlevel/type,abort+*processingandreturntheacpi_pptt_cachestructure.+*/+staticstructacpi_pptt_cache*acpi_find_cache_level(+structacpi_table_header*table_hdr,+structacpi_pptt_processor*cpu_node,+int*starting_level,intlevel,inttype)+{+structacpi_subtable_header*res;+intnumber_of_levels=*starting_level;+intresource=0;+structacpi_pptt_cache*ret=NULL;+intlocal_level;++/* walk down from processor node */+while((res=acpi_get_pptt_resource(table_hdr,cpu_node,resource))){+resource++;++local_level=acpi_pptt_walk_cache(table_hdr,*starting_level,+res,&ret,level,type);+/*+*wearelookingforthemaxdepth.Sinceitspotentially+*possibleforagivennodetohaveresourceswithdiffering+*depthsverifythatthedepthwehavefoundisthelargest.+*/+if(number_of_levels<local_level)+number_of_levels=local_level;+}+if(number_of_levels>*starting_level)+*starting_level=number_of_levels;++returnret;+}++/*+*givenaprocessornodecontainingaprocessingunit,walkintoitandcount+*howmanylevelsexistsolelyforit,andthenwalkupeachleveluntilwehit+*therootnode(ignorethepackagelevelbecauseitmaybepossibletohave+*cachesthatexistacrosspackages).Countthenumberofcachelevelsthat+*existateachlevelonthewayup.+*/+staticintacpi_process_node(structacpi_table_header*table_hdr,+structacpi_pptt_processor*cpu_node)+{+inttotal_levels=0;++do{+acpi_find_cache_level(table_hdr,cpu_node,&total_levels,0,0);+cpu_node=fetch_pptt_node(table_hdr,cpu_node->parent);+}while(cpu_node);++returntotal_levels;+}++/*+*Findthesubtableentrydescribingtheprovidedprocessor+*/+staticstructacpi_pptt_processor*acpi_find_processor_node(+structacpi_table_header*table_hdr,+u32acpi_cpu_id)+{+structacpi_subtable_header*entry;+unsignedlongtable_end;+structacpi_pptt_processor*cpu_node;++table_end=(unsignedlong)table_hdr+table_hdr->length;+entry=(structacpi_subtable_header*)((u8*)table_hdr++sizeof(structacpi_table_pptt));++/* find the processor structure associated with this cpuid */+while(((unsignedlong)entry)+sizeof(structacpi_subtable_header)<table_end){+cpu_node=(structacpi_pptt_processor*)entry;++if((entry->type==ACPI_PPTT_TYPE_PROCESSOR)&&+(cpu_node->flags&ACPI_PPTT_ACPI_PROCESSOR_ID_VALID)){+pr_debug("checking phy_cpu_id %d against acpi id %d\n",+acpi_cpu_id,cpu_node->acpi_processor_id);+if(acpi_cpu_id==cpu_node->acpi_processor_id){+/* found the correct entry */+pr_debug("match found!\n");+return(structacpi_pptt_processor*)entry;+}+}++if(entry->length==0){+pr_err("Invalid zero length subtable\n");+break;+}+entry=(structacpi_subtable_header*)+((u8*)entry+entry->length);+}++returnNULL;+}++/*+*Countthetotalnumberofprocessornodesthataremarkedasphysical+*packages.Thisshouldequalthenumberofsocketsinthemachine.+*/+staticintacpi_count_socket_nodes(structacpi_table_header*table_hdr)+{+structacpi_subtable_header*entry;+unsignedlongtable_end;+structacpi_pptt_processor*cpu_node;+intnumber_of_sockets=0;++table_end=(unsignedlong)table_hdr+table_hdr->length;+entry=(structacpi_subtable_header*)((u8*)table_hdr++sizeof(structacpi_table_pptt));++/* count processor structures with PHYSICAL_PACKAGE set */+while(((unsignedlong)entry)+sizeof(structacpi_subtable_header)<table_end){+cpu_node=(structacpi_pptt_processor*)entry;++if((entry->type==ACPI_PPTT_TYPE_PROCESSOR)&&+(cpu_node->flags&ACPI_PPTT_PHYSICAL_PACKAGE))+number_of_sockets++;++if(entry->length==0){+pr_err("Invalid zero length subtable\n");+break;+}+entry=(structacpi_subtable_header*)+((u8*)entry+entry->length);+}++returnnumber_of_sockets;+}+++/*+*Givenaacpi_pptt_processornode,walkupuntilweidentifythe+*packagethatthenodeisassociatedwithorwerunoutoflevels+*torequest.+*/+staticstructacpi_pptt_processor*acpi_find_processor_package_id(+structacpi_table_header*table_hdr,+structacpi_pptt_processor*cpu,+intlevel)+{+structacpi_pptt_processor*prev_node;++while(cpu&&level&&!(cpu->flags&ACPI_PPTT_PHYSICAL_PACKAGE)){+pr_debug("level %d\n",level);+prev_node=fetch_pptt_node(table_hdr,cpu->parent);+if(prev_node==NULL)+break;+cpu=prev_node;+level--;+}+returncpu;+}++staticintacpi_parse_pptt(structacpi_table_header*table_hdr,u32acpi_cpu_id)+{+intnumber_of_levels=0;+structacpi_pptt_processor*cpu;++cpu=acpi_find_processor_node(table_hdr,acpi_cpu_id);+if(cpu)+number_of_levels=acpi_process_node(table_hdr,cpu);++returnnumber_of_levels;+}++#define ACPI_6_2_CACHE_TYPE_DATA (0x0)+#define ACPI_6_2_CACHE_TYPE_INSTR (1<<2)+#define ACPI_6_2_CACHE_TYPE_UNIFIED (1<<3)+#define ACPI_6_2_CACHE_POLICY_WB (0x0)+#define ACPI_6_2_CACHE_POLICY_WT (1<<4)+#define ACPI_6_2_CACHE_READ_ALLOCATE (0x0)+#define ACPI_6_2_CACHE_WRITE_ALLOCATE (0x01)+#define ACPI_6_2_CACHE_RW_ALLOCATE (0x02)++staticu8acpi_cache_type(enumcache_typetype)+{+switch(type){+caseCACHE_TYPE_DATA:+pr_debug("Looking for data cache\n");+returnACPI_6_2_CACHE_TYPE_DATA;+caseCACHE_TYPE_INST:+pr_debug("Looking for instruction cache\n");+returnACPI_6_2_CACHE_TYPE_INSTR;+default:+pr_debug("Unknown cache type, assume unified\n");+caseCACHE_TYPE_UNIFIED:+pr_debug("Looking for unified cache\n");+returnACPI_6_2_CACHE_TYPE_UNIFIED;+}+}++/* find the ACPI node describing the cache type/level for the given CPU */+staticstructacpi_pptt_cache*acpi_find_cache_node(+structacpi_table_header*table_hdr,u32acpi_cpu_id,+enumcache_typetype,unsignedintlevel)+{+inttotal_levels=0;+structacpi_pptt_cache*found=NULL;+structacpi_pptt_processor*cpu_node;+u8acpi_type=acpi_cache_type(type);++pr_debug("Looking for CPU %d's level %d cache type %d\n",+acpi_cpu_id,level,acpi_type);++cpu_node=acpi_find_processor_node(table_hdr,acpi_cpu_id);+if(!cpu_node)+returnNULL;++do{+found=acpi_find_cache_level(table_hdr,cpu_node,&total_levels,level,acpi_type);+cpu_node=fetch_pptt_node(table_hdr,cpu_node->parent);+}while((cpu_node)&&(!found));++returnfound;+}++intacpi_find_last_cache_level(unsignedintcpu)+{+u32acpi_cpu_id;+structacpi_table_header*table;+intnumber_of_levels=0;+acpi_statusstatus;++pr_debug("Cache Setup find last level cpu=%d\n",cpu);++acpi_cpu_id=acpi_cpu_get_madt_gicc(cpu)->uid;+status=acpi_get_table(ACPI_SIG_PPTT,0,&table);+if(ACPI_FAILURE(status)){+pr_err_once("No PPTT table found, cache topology may be inaccurate\n");+}else{+number_of_levels=acpi_parse_pptt(table,acpi_cpu_id);+acpi_put_table(table);+}+pr_debug("Cache Setup find last level level=%d\n",number_of_levels);++returnnumber_of_levels;+}++/*+*TheACPIspecimpliesthatthefieldsinthecachestructuresareusedto+*extendandcorrecttheinformationprobedfromthehardware.Inthecase+*ofarm64theCCSIDRprobinghasbeenremovedbecauseitmightbeincorrect.+*/+staticvoidupdate_cache_properties(structcacheinfo*this_leaf,+structacpi_pptt_cache*found_cache)+{+if(found_cache->flags&ACPI_PPTT_SIZE_PROPERTY_VALID)+this_leaf->size=found_cache->size;+if(found_cache->flags&ACPI_PPTT_LINE_SIZE_VALID)+this_leaf->coherency_line_size=found_cache->line_size;+if(found_cache->flags&ACPI_PPTT_NUMBER_OF_SETS_VALID)+this_leaf->number_of_sets=found_cache->number_of_sets;+if(found_cache->flags&ACPI_PPTT_ASSOCIATIVITY_VALID)+this_leaf->ways_of_associativity=found_cache->associativity;+if(found_cache->flags&ACPI_PPTT_WRITE_POLICY_VALID)+switch(found_cache->attributes&ACPI_PPTT_MASK_WRITE_POLICY){+caseACPI_6_2_CACHE_POLICY_WT:+this_leaf->attributes=CACHE_WRITE_THROUGH;+break;+caseACPI_6_2_CACHE_POLICY_WB:+this_leaf->attributes=CACHE_WRITE_BACK;+break;+default:+pr_err("Unknown ACPI cache policy %d\n",+found_cache->attributes&ACPI_PPTT_MASK_WRITE_POLICY);+}+if(found_cache->flags&ACPI_PPTT_ALLOCATION_TYPE_VALID)+switch(found_cache->attributes&ACPI_PPTT_MASK_ALLOCATION_TYPE){+caseACPI_6_2_CACHE_READ_ALLOCATE:+this_leaf->attributes|=CACHE_READ_ALLOCATE;+break;+caseACPI_6_2_CACHE_WRITE_ALLOCATE:+this_leaf->attributes|=CACHE_WRITE_ALLOCATE;+break;+caseACPI_6_2_CACHE_RW_ALLOCATE:+this_leaf->attributes|=+CACHE_READ_ALLOCATE|CACHE_WRITE_ALLOCATE;+break;+default:+pr_err("Unknown ACPI cache allocation policy %d\n",+found_cache->attributes&ACPI_PPTT_MASK_ALLOCATION_TYPE);+}+}++staticvoidcache_setup_acpi_cpu(structacpi_table_header*table,+unsignedintcpu)+{+structacpi_pptt_cache*found_cache;+structcpu_cacheinfo*this_cpu_ci=get_cpu_cacheinfo(cpu);+u32acpi_cpu_id=acpi_cpu_get_madt_gicc(cpu)->uid;+structcacheinfo*this_leaf;+unsignedintindex=0;++while(index<get_cpu_cacheinfo(cpu)->num_leaves){+this_leaf=this_cpu_ci->info_list+index;+found_cache=acpi_find_cache_node(table,acpi_cpu_id,+this_leaf->type,+this_leaf->level);+pr_debug("found = %p\n",found_cache);+if(found_cache)+update_cache_properties(this_leaf,found_cache);++index++;+}+}++staticinttopology_setup_acpi_cpu(structacpi_table_header*table,+unsignedintcpu,intlevel)+{+structacpi_pptt_processor*cpu_node;+u32acpi_cpu_id=acpi_cpu_get_madt_gicc(cpu)->uid;++cpu_node=acpi_find_processor_node(table,acpi_cpu_id);+if(cpu_node){+cpu_node=acpi_find_processor_package_id(table,cpu_node,level);+return(int)((u8*)cpu_node-(u8*)table);+}+pr_err_once("PPTT table found, but unable to locate core for %d\n",+cpu);+return-ENOENT;+}++/*+*simplyassignaACPIcacheentrytoeachknownCPUcacheentry+*determiningwhichentriesaresharedisdonelater.+*/+intcache_setup_acpi(unsignedintcpu)+{+structacpi_table_header*table;+acpi_statusstatus;++pr_debug("Cache Setup ACPI cpu %d\n",cpu);++status=acpi_get_table(ACPI_SIG_PPTT,0,&table);+if(ACPI_FAILURE(status)){+pr_err_once("No PPTT table found, cache topology may be inaccurate\n");+return-ENOENT;+}++cache_setup_acpi_cpu(table,cpu);+acpi_put_table(table);++returnstatus;+}++/*+*DetermineatopologyuniqueIDforeachthread/core/cluster/socket/etc.+*ThisIDcanthenbeusedtogrouppeers.+*/+intsetup_acpi_cpu_topology(unsignedintcpu,intlevel)+{+structacpi_table_header*table;+acpi_statusstatus;+intretval;++status=acpi_get_table(ACPI_SIG_PPTT,0,&table);+if(ACPI_FAILURE(status)){+pr_err_once("No PPTT table found, cpu topology may be inaccurate\n");+return-ENOENT;+}+retval=topology_setup_acpi_cpu(table,cpu,level);+pr_debug("Topology Setup ACPI cpu %d, level %d ret = %d\n",+cpu,level,retval);+acpi_put_table(table);++returnretval;+}++/*+*WalkthePPTT,countthenumberofsocketswedetect+*/+intacpi_multisocket_count(void)+{+structacpi_table_header*table;+acpi_statusstatus;+intretval=0;++status=acpi_get_table(ACPI_SIG_PPTT,0,&table);+if(ACPI_FAILURE(status)){+pr_err_once("No PPTT table found, socket topology may be inaccurate\n");+return-ENOENT;+}+retval=acpi_count_socket_nodes(table);+acpi_put_table(table);++returnretval;+}
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:07
Now that we have a PPTT parser, in preparation for its use
on arm64, lets build it.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/Kconfig | 1 +
drivers/acpi/Makefile | 1 +
drivers/acpi/arm64/Kconfig | 3 +++
3 files changed, 5 insertions(+)
@@ -85,6 +85,7 @@ obj-$(CONFIG_ACPI_BGRT) += bgrt.oobj-$(CONFIG_ACPI_CPPC_LIB)+=cppc_acpi.oobj-$(CONFIG_ACPI_SPCR_TABLE)+=spcr.oobj-$(CONFIG_ACPI_DEBUGGER_USER)+=acpi_dbg.o+obj-$(CONFIG_ACPI_PPTT)+=pptt.o# processor has its own "processor." module_param namespaceprocessor-y:=processor_driver.o
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:08
The /sys cache entries should support ACPI/PPTT generated cache
topology information. Lets detect ACPI systems and call
an arch specific cache_setup_acpi() routine to update the hardware
probed cache topology.
For arm64, if ACPI is enabled, determine the max number of cache
levels and populate them using a PPTT table if one is available.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/cacheinfo.c | 23 ++++++++++++++++++-----
drivers/acpi/pptt.c | 1 +
drivers/base/cacheinfo.c | 17 +++++++++++------
include/linux/cacheinfo.h | 10 ++++++++--
4 files changed, 38 insertions(+), 13 deletions(-)
@@ -44,9 +45,17 @@ static void ci_leaf_init(struct cacheinfo *this_leaf,this_leaf->type=type;}+#ifndef CONFIG_ACPI+intacpi_find_last_cache_level(unsignedintcpu)+{+/*ACPI kernels should be built with PPTT support*/+return0;+}+#endif+staticint__init_cache_level(unsignedintcpu){-unsignedintctype,level,leaves,of_level;+unsignedintctype,level,leaves,fw_level;structcpu_cacheinfo*this_cpu_ci=get_cpu_cacheinfo(cpu);for(level=1,leaves=0;level<=MAX_CACHE_LEVEL;level++){
@@ -59,15 +68,19 @@ static int __init_cache_level(unsigned int cpu)leaves+=(ctype==CACHE_TYPE_SEPARATE)?2:1;}-of_level=of_find_last_cache_level(cpu);-if(level<of_level){+if(acpi_disabled)+fw_level=of_find_last_cache_level(cpu);+else+fw_level=acpi_find_last_cache_level(cpu);++if(level<fw_level){/**someexternalcachesnotspecifiedinCLIDR_EL1*theinformationmaybeavailableinthedevicetree*onlyunifiedexternalcachesareconsideredhere*/-leaves+=(of_level-level);-level=of_level;+leaves+=(fw_level-level);+level=fw_level;}this_cpu_ci->num_levels=level;
@@ -364,6 +364,7 @@ int acpi_find_last_cache_level(unsigned int cpu)staticvoidupdate_cache_properties(structcacheinfo*this_leaf,structacpi_pptt_cache*found_cache){+this_leaf->firmware_node=found_cache;if(found_cache->flags&ACPI_PPTT_SIZE_PROPERTY_VALID)this_leaf->size=found_cache->size;if(found_cache->flags&ACPI_PPTT_LINE_SIZE_VALID)
@@ -86,7 +86,7 @@ static int cache_setup_of_node(unsigned int cpu)staticinlineboolcache_leaves_are_shared(structcacheinfo*this_leaf,structcacheinfo*sib_leaf){-returnsib_leaf->of_node==this_leaf->of_node;+returnsib_leaf->firmware_node==this_leaf->firmware_node;}/* OF properties to query for a given cache type */
@@ -225,11 +230,11 @@ static int cache_shared_cpu_map_setup(unsigned int cpu)if(this_cpu_ci->cpu_map_populated)return0;-if(of_have_populated_dt())+if(!acpi_disabled)+ret=cache_setup_acpi(cpu);+elseif(of_have_populated_dt())ret=cache_setup_of_node(cpu);-elseif(!acpi_disabled)-/* No cache property/hierarchy support yet in ACPI */-ret=-ENOTSUPP;+if(ret)returnret;
@@ -286,7 +291,7 @@ static void cache_shared_cpu_map_remove(unsigned int cpu)staticvoidcache_override_properties(unsignedintcpu){-if(of_have_populated_dt())+if(acpi_disabled&&of_have_populated_dt())returncache_of_override_properties(cpu);}
@@ -98,6 +103,7 @@ int func(unsigned int cpu) \structcpu_cacheinfo*get_cpu_cacheinfo(unsignedintcpu);intinit_cache_level(unsignedintcpu);intpopulate_cache_leaves(unsignedintcpu);+intacpi_find_last_cache_level(unsignedintcpu);conststructattribute_group*cache_get_priv_group(structcacheinfo*this_leaf);
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:09
Many modern machines have cluster on die (COD) non-uniformity
as well as the traditional multi-socket architectures. Reusing
the multi-socket or NUMA on die concepts for these (as arm64 does)
breaks down when presented with actual multi-socket/COD machines.
Similar, problems are also visible on some x86 machines so it
seems appropriate to start abstracting and making these topologies
visible.
To start, a topology_cod_id() macro is added which defaults to returning
the same information as topology_physical_package_id(). Moving forward
we can start to spit out the differences.
For arm64, an additional package_id is added to the cpu_topology array.
Initially this will be equal to the cluster_id as well.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/include/asm/topology.h | 4 +++-
arch/arm64/kernel/topology.c | 8 ++++++--
include/linux/topology.h | 3 +++
3 files changed, 12 insertions(+), 3 deletions(-)
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:10
There are a few arm64 specific users (cpufreq, psci, etc) which really
want the cluster rather than the topology_physical_package_id(). Lets
convert those users to topology_cod_id(). That way when we start
differentiating the socket/cluster they will continue to behave correctly.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/clk/clk-mb86s7x.c | 2 +-
drivers/cpufreq/arm_big_little.c | 2 +-
drivers/firmware/psci_checker.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:11
Propagate the topology information from the PPTT tree to the
cpu_topology array. We can get the thread id, core_id and
cluster_id by assuming certain levels of the PPTT tree correspond
to those concepts. The package_id is flagged in the tree and can be
found by passing an arbitrary large level to setup_acpi_cpu_topology()
which terminates its search when it finds an ACPI node flagged
as the physical package. If the tree doesn't contain enough
levels to represent all of thread/core/cod/package then the package
id will be used for the missing levels.
Since arm64 machines can have 3 distinct topology levels, and the
scheduler only handles sockets/threads well today, we compromise
by collapsing into one of three diffrent configurations. These are
thread/socket, thread/cluster or cluster/socket depending on whether
the machine has threading and multisocket, threading in a single
socket, or doesn't have threading.
This code is loosely based on a combination of code from:
Xiongfeng Wang [off-list ref]
John Garry [off-list ref]
Jeffrey Hugo [off-list ref]
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/topology.c | 68 +++++++++++++++++++++++++++++++++++++++++++-
include/linux/topology.h | 2 ++
2 files changed, 69 insertions(+), 1 deletion(-)
@@ -304,6 +306,68 @@ static void __init reset_cpu_topology(void)}}+#ifdef CONFIG_ACPI+/*+*Propagatethetopologyinformationoftheprocessor_topology_nodetreetothe+*cpu_topologyarray.+*/+staticint__initparse_acpi_topology(void)+{+u64is_threaded;+intis_multisocket;+intcpu;+inttopology_id;+/* set a large depth, to hit ACPI_PPTT_PHYSICAL_PACKAGE if one exists */+constintmax_topo=0xFF;++is_threaded=read_cpuid_mpidr()&MPIDR_MT_BITMASK;+is_multisocket=acpi_multisocket_count();+if(is_multisocket<0)+returnis_multisocket;++for_each_possible_cpu(cpu){+topology_id=setup_acpi_cpu_topology(cpu,0);+if(topology_id<0)+returntopology_id;++if((is_threaded)&&(is_multisocket>1)){+/* MT per core, and multiple sockets */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,2);+cpu_topology[cpu].cluster_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,max_topo);+cpu_topology[cpu].package_id=topology_id;+}elseif(is_threaded){+/* mutltiple threads, but only a single socket */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,2);+cpu_topology[cpu].cluster_id=topology_id;+cpu_topology[cpu].package_id=topology_id;+}else{+/* no threads, clusters behave like threads */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+cpu_topology[cpu].cluster_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,max_topo);+cpu_topology[cpu].package_id=topology_id;+}+}+return0;+}++#else+staticint__initparse_acpi_topology(void)+{+/*ACPI kernels should be built with PPTT support*/+return-EINVAL;+}+#endif+void__initinit_cpu_topology(void){reset_cpu_topology();
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:12
ACPI 6.2 adds the Processor Properties Topology Table (PPTT), which is
used to describe the processor and cache topologies. Ideally it is
used to extend/override information provided by the hardware, but
right now ARM64 is entirely dependent on firmware provided tables.
This patch parses the table for the cache topology and CPU topology.
For the latter we also add an additional topology_cod_id() macro,
and a package_id for arm64. Initially the physical id will match
the cluster id, but we update users of the cluster to utilize
the new macro. When we enable PPTT for the arm64 the cluster/socket
starts to differ. Because of this we also make some dynamic decisions
about mapping thread/core/cod/socket to the thread/socket used by the
scheduler.
For example on juno:
[root at mammon-juno-rh topology]# lstopo-no-graphics
Machine (7048MB)
Package L#0
L2 L#0 (1024KB) + Core L#0
L1d L#0 (32KB) + L1i L#0 (32KB) + PU L#0 (P#0)
L1d L#1 (32KB) + L1i L#1 (32KB) + PU L#1 (P#1)
L1d L#2 (32KB) + L1i L#2 (32KB) + PU L#2 (P#2)
L1d L#3 (32KB) + L1i L#3 (32KB) + PU L#3 (P#3)
L2 L#1 (2048KB) + Core L#1
L1d L#4 (32KB) + L1i L#4 (48KB) + PU L#4 (P#4)
L1d L#5 (32KB) + L1i L#5 (48KB) + PU L#5 (P#5)
HostBridge L#0
PCIBridge
PCIBridge
PCIBridge
PCI 1095:3132
Block(Disk) L#0 "sda"
PCIBridge
PCI 1002:68f9
GPU L#1 "renderD128"
GPU L#2 "card0"
GPU L#3 "controlD64"
PCIBridge
PCI 11ab:4380
Net L#4 "enp8s0"
Jeremy Linton (6):
ACPI/PPTT: Add Processor Properties Topology Table parsing
ACPI: Enable PPTT support on ARM64
drivers: base: cacheinfo: arm64: Add support for ACPI based firmware
tables
Topology: Add cluster on die macros and arm64 decoding
arm64: Fixup users of topology_physical_package_id
arm64: topology: Enable ACPI/PPTT based CPU topology.
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/topology.h | 4 +-
arch/arm64/kernel/cacheinfo.c | 23 +-
arch/arm64/kernel/topology.c | 76 +++++-
drivers/acpi/Makefile | 1 +
drivers/acpi/arm64/Kconfig | 3 +
drivers/acpi/pptt.c | 508 ++++++++++++++++++++++++++++++++++++++
drivers/base/cacheinfo.c | 17 +-
drivers/clk/clk-mb86s7x.c | 2 +-
drivers/cpufreq/arm_big_little.c | 2 +-
drivers/firmware/psci_checker.c | 2 +-
include/linux/cacheinfo.h | 10 +-
include/linux/topology.h | 5 +
13 files changed, 634 insertions(+), 20 deletions(-)
create mode 100644 drivers/acpi/pptt.c
--
2.13.5
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:13
ACPI 6.2 adds a new table, which describes how processing units
are related to each other in tree like fashion. Caches are
also sprinkled throughout the tree and describe the properties
of the caches in relation to other caches and processing units.
Add the code to parse the cache hierarchy and report the total
number of levels of cache for a given core using
acpi_find_last_cache_level() as well as fill out the individual
cores cache information with cache_setup_acpi() once the
cpu_cacheinfo structure has been populated by the arch specific
code.
Further, report peers in the topology using setup_acpi_cpu_topology()
to report a unique ID for each processing unit at a given level
in the tree. These unique id's can then be used to match related
processing units which exist as threads, COD (clusters
on die), within a given package, etc.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/acpi/pptt.c | 507 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 507 insertions(+)
create mode 100644 drivers/acpi/pptt.c
@@ -0,0 +1,507 @@+/*+*Copyright(C)2017,ARM+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsandconditionsoftheGNUGeneralPublicLicense,+*version2,aspublishedbytheFreeSoftwareFoundation.+*+*Thisprogramisdistributedinthehopeitwillbeuseful,butWITHOUT+*ANYWARRANTY;withouteventheimpliedwarrantyofMERCHANTABILITYor+*FITNESSFORAPARTICULARPURPOSE.SeetheGNUGeneralPublicLicensefor+*moredetails.+*+*ThisfileimplementsparsingofProcessorPropertiesTopologyTable(PPTT)+*whichisoptionallyusedtodescribetheprocessorandcachetopology.+*Duetotherelativepointersusedthroughoutthetable,thisdoesn't+*leveragetheexistingsubtableparsinginthekernel.+*/++#define pr_fmt(fmt) "ACPI PPTT: " fmt++#include<linux/acpi.h>+#include<linux/cacheinfo.h>+#include<acpi/processor.h>++/*+*GiventhePPTTtable,findandverifythatthesubtableentry+*islocatedwithinthetable+*/+staticstructacpi_subtable_header*fetch_pptt_subtable(+structacpi_table_header*table_hdr,u32pptt_ref)+{+structacpi_subtable_header*entry;++/* there isn't a subtable at reference 0 */+if(!pptt_ref)+returnNULL;++if(pptt_ref+sizeof(structacpi_subtable_header)>table_hdr->length)+returnNULL;++entry=(structacpi_subtable_header*)((u8*)table_hdr+pptt_ref);++if(pptt_ref+entry->length>table_hdr->length)+returnNULL;++returnentry;+}++staticstructacpi_pptt_processor*fetch_pptt_node(+structacpi_table_header*table_hdr,u32pptt_ref)+{+return(structacpi_pptt_processor*)fetch_pptt_subtable(table_hdr,pptt_ref);+}++staticstructacpi_pptt_cache*fetch_pptt_cache(+structacpi_table_header*table_hdr,u32pptt_ref)+{+return(structacpi_pptt_cache*)fetch_pptt_subtable(table_hdr,pptt_ref);+}++staticstructacpi_subtable_header*acpi_get_pptt_resource(+structacpi_table_header*table_hdr,+structacpi_pptt_processor*node,intresource)+{+u32ref;++if(resource>=node->number_of_priv_resources)+returnNULL;++ref=*(u32*)((u8*)node+sizeof(structacpi_pptt_processor)++sizeof(u32)*resource);++returnfetch_pptt_subtable(table_hdr,ref);+}++/*+*givenappttresource,verifythatitisacachenode,thenwalk+*downeachlevelofcaches,countinghowmanylevelsarefound+*aswellascheckingthecachetype(icache,dcache,unified).Ifa+*level&typematch,thenwesetfound,andcontinuethesearch.+*Oncetheentirecachebranchhasbeenwalkedreturnitsmax+*depth.+*/+staticintacpi_pptt_walk_cache(structacpi_table_header*table_hdr,+intlocal_level,+structacpi_subtable_header*res,+structacpi_pptt_cache**found,+intlevel,inttype)+{+structacpi_pptt_cache*cache;++if(res->type!=ACPI_PPTT_TYPE_CACHE)+return0;++cache=(structacpi_pptt_cache*)res;+while(cache){+local_level++;++if((local_level==level)&&+(cache->flags&ACPI_PPTT_CACHE_TYPE_VALID)&&+((cache->attributes&ACPI_PPTT_MASK_CACHE_TYPE)==type)){+if(*found!=NULL)+pr_err("Found duplicate cache level/type unable to determine uniqueness\n");++pr_debug("Found cache @ level %d\n",level);+*found=cache;+/*+*continuelookingatthisnode'sresourcelist+*toverifythatwedon'tfindaduplicate+*cachenode.+*/+}+cache=fetch_pptt_cache(table_hdr,cache->next_level_of_cache);+}+returnlocal_level;+}++/*+*GivenaCPUnodelookforcachelevelsthatexistatthislevel,andthen+*foreachcachenode,counthowmanylevelsexistbelow(logicallyabove)it.+*Ifalevelandtypearespecified,andwefindthatlevel/type,abort+*processingandreturntheacpi_pptt_cachestructure.+*/+staticstructacpi_pptt_cache*acpi_find_cache_level(+structacpi_table_header*table_hdr,+structacpi_pptt_processor*cpu_node,+int*starting_level,intlevel,inttype)+{+structacpi_subtable_header*res;+intnumber_of_levels=*starting_level;+intresource=0;+structacpi_pptt_cache*ret=NULL;+intlocal_level;++/* walk down from processor node */+while((res=acpi_get_pptt_resource(table_hdr,cpu_node,resource))){+resource++;++local_level=acpi_pptt_walk_cache(table_hdr,*starting_level,+res,&ret,level,type);+/*+*wearelookingforthemaxdepth.Sinceitspotentially+*possibleforagivennodetohaveresourceswithdiffering+*depthsverifythatthedepthwehavefoundisthelargest.+*/+if(number_of_levels<local_level)+number_of_levels=local_level;+}+if(number_of_levels>*starting_level)+*starting_level=number_of_levels;++returnret;+}++/*+*givenaprocessornodecontainingaprocessingunit,walkintoitandcount+*howmanylevelsexistsolelyforit,andthenwalkupeachleveluntilwehit+*therootnode(ignorethepackagelevelbecauseitmaybepossibletohave+*cachesthatexistacrosspackages).Countthenumberofcachelevelsthat+*existateachlevelonthewayup.+*/+staticintacpi_process_node(structacpi_table_header*table_hdr,+structacpi_pptt_processor*cpu_node)+{+inttotal_levels=0;++do{+acpi_find_cache_level(table_hdr,cpu_node,&total_levels,0,0);+cpu_node=fetch_pptt_node(table_hdr,cpu_node->parent);+}while(cpu_node);++returntotal_levels;+}++/*+*Findthesubtableentrydescribingtheprovidedprocessor+*/+staticstructacpi_pptt_processor*acpi_find_processor_node(+structacpi_table_header*table_hdr,+u32acpi_cpu_id)+{+structacpi_subtable_header*entry;+unsignedlongtable_end;+structacpi_pptt_processor*cpu_node;++table_end=(unsignedlong)table_hdr+table_hdr->length;+entry=(structacpi_subtable_header*)((u8*)table_hdr++sizeof(structacpi_table_pptt));++/* find the processor structure associated with this cpuid */+while(((unsignedlong)entry)+sizeof(structacpi_subtable_header)<table_end){+cpu_node=(structacpi_pptt_processor*)entry;++if((entry->type==ACPI_PPTT_TYPE_PROCESSOR)&&+(cpu_node->flags&ACPI_PPTT_ACPI_PROCESSOR_ID_VALID)){+pr_debug("checking phy_cpu_id %d against acpi id %d\n",+acpi_cpu_id,cpu_node->acpi_processor_id);+if(acpi_cpu_id==cpu_node->acpi_processor_id){+/* found the correct entry */+pr_debug("match found!\n");+return(structacpi_pptt_processor*)entry;+}+}++if(entry->length==0){+pr_err("Invalid zero length subtable\n");+break;+}+entry=(structacpi_subtable_header*)+((u8*)entry+entry->length);+}++returnNULL;+}++/*+*Countthetotalnumberofprocessornodesthataremarkedasphysical+*packages.Thisshouldequalthenumberofsocketsinthemachine.+*/+staticintacpi_count_socket_nodes(structacpi_table_header*table_hdr)+{+structacpi_subtable_header*entry;+unsignedlongtable_end;+structacpi_pptt_processor*cpu_node;+intnumber_of_sockets=0;++table_end=(unsignedlong)table_hdr+table_hdr->length;+entry=(structacpi_subtable_header*)((u8*)table_hdr++sizeof(structacpi_table_pptt));++/* count processor structures with PHYSICAL_PACKAGE set */+while(((unsignedlong)entry)+sizeof(structacpi_subtable_header)<table_end){+cpu_node=(structacpi_pptt_processor*)entry;++if((entry->type==ACPI_PPTT_TYPE_PROCESSOR)&&+(cpu_node->flags&ACPI_PPTT_PHYSICAL_PACKAGE))+number_of_sockets++;++if(entry->length==0){+pr_err("Invalid zero length subtable\n");+break;+}+entry=(structacpi_subtable_header*)+((u8*)entry+entry->length);+}++returnnumber_of_sockets;+}+++/*+*Givenaacpi_pptt_processornode,walkupuntilweidentifythe+*packagethatthenodeisassociatedwithorwerunoutoflevels+*torequest.+*/+staticstructacpi_pptt_processor*acpi_find_processor_package_id(+structacpi_table_header*table_hdr,+structacpi_pptt_processor*cpu,+intlevel)+{+structacpi_pptt_processor*prev_node;++while(cpu&&level&&!(cpu->flags&ACPI_PPTT_PHYSICAL_PACKAGE)){+pr_debug("level %d\n",level);+prev_node=fetch_pptt_node(table_hdr,cpu->parent);+if(prev_node==NULL)+break;+cpu=prev_node;+level--;+}+returncpu;+}++staticintacpi_parse_pptt(structacpi_table_header*table_hdr,u32acpi_cpu_id)+{+intnumber_of_levels=0;+structacpi_pptt_processor*cpu;++cpu=acpi_find_processor_node(table_hdr,acpi_cpu_id);+if(cpu)+number_of_levels=acpi_process_node(table_hdr,cpu);++returnnumber_of_levels;+}++#define ACPI_6_2_CACHE_TYPE_DATA (0x0)+#define ACPI_6_2_CACHE_TYPE_INSTR (1<<2)+#define ACPI_6_2_CACHE_TYPE_UNIFIED (1<<3)+#define ACPI_6_2_CACHE_POLICY_WB (0x0)+#define ACPI_6_2_CACHE_POLICY_WT (1<<4)+#define ACPI_6_2_CACHE_READ_ALLOCATE (0x0)+#define ACPI_6_2_CACHE_WRITE_ALLOCATE (0x01)+#define ACPI_6_2_CACHE_RW_ALLOCATE (0x02)++staticu8acpi_cache_type(enumcache_typetype)+{+switch(type){+caseCACHE_TYPE_DATA:+pr_debug("Looking for data cache\n");+returnACPI_6_2_CACHE_TYPE_DATA;+caseCACHE_TYPE_INST:+pr_debug("Looking for instruction cache\n");+returnACPI_6_2_CACHE_TYPE_INSTR;+default:+pr_debug("Unknown cache type, assume unified\n");+caseCACHE_TYPE_UNIFIED:+pr_debug("Looking for unified cache\n");+returnACPI_6_2_CACHE_TYPE_UNIFIED;+}+}++/* find the ACPI node describing the cache type/level for the given CPU */+staticstructacpi_pptt_cache*acpi_find_cache_node(+structacpi_table_header*table_hdr,u32acpi_cpu_id,+enumcache_typetype,unsignedintlevel)+{+inttotal_levels=0;+structacpi_pptt_cache*found=NULL;+structacpi_pptt_processor*cpu_node;+u8acpi_type=acpi_cache_type(type);++pr_debug("Looking for CPU %d's level %d cache type %d\n",+acpi_cpu_id,level,acpi_type);++cpu_node=acpi_find_processor_node(table_hdr,acpi_cpu_id);+if(!cpu_node)+returnNULL;++do{+found=acpi_find_cache_level(table_hdr,cpu_node,&total_levels,level,acpi_type);+cpu_node=fetch_pptt_node(table_hdr,cpu_node->parent);+}while((cpu_node)&&(!found));++returnfound;+}++intacpi_find_last_cache_level(unsignedintcpu)+{+u32acpi_cpu_id;+structacpi_table_header*table;+intnumber_of_levels=0;+acpi_statusstatus;++pr_debug("Cache Setup find last level cpu=%d\n",cpu);++acpi_cpu_id=acpi_cpu_get_madt_gicc(cpu)->uid;+status=acpi_get_table(ACPI_SIG_PPTT,0,&table);+if(ACPI_FAILURE(status)){+pr_err_once("No PPTT table found, cache topology may be inaccurate\n");+}else{+number_of_levels=acpi_parse_pptt(table,acpi_cpu_id);+acpi_put_table(table);+}+pr_debug("Cache Setup find last level level=%d\n",number_of_levels);++returnnumber_of_levels;+}++/*+*TheACPIspecimpliesthatthefieldsinthecachestructuresareusedto+*extendandcorrecttheinformationprobedfromthehardware.Inthecase+*ofarm64theCCSIDRprobinghasbeenremovedbecauseitmightbeincorrect.+*/+staticvoidupdate_cache_properties(structcacheinfo*this_leaf,+structacpi_pptt_cache*found_cache)+{+if(found_cache->flags&ACPI_PPTT_SIZE_PROPERTY_VALID)+this_leaf->size=found_cache->size;+if(found_cache->flags&ACPI_PPTT_LINE_SIZE_VALID)+this_leaf->coherency_line_size=found_cache->line_size;+if(found_cache->flags&ACPI_PPTT_NUMBER_OF_SETS_VALID)+this_leaf->number_of_sets=found_cache->number_of_sets;+if(found_cache->flags&ACPI_PPTT_ASSOCIATIVITY_VALID)+this_leaf->ways_of_associativity=found_cache->associativity;+if(found_cache->flags&ACPI_PPTT_WRITE_POLICY_VALID)+switch(found_cache->attributes&ACPI_PPTT_MASK_WRITE_POLICY){+caseACPI_6_2_CACHE_POLICY_WT:+this_leaf->attributes=CACHE_WRITE_THROUGH;+break;+caseACPI_6_2_CACHE_POLICY_WB:+this_leaf->attributes=CACHE_WRITE_BACK;+break;+default:+pr_err("Unknown ACPI cache policy %d\n",+found_cache->attributes&ACPI_PPTT_MASK_WRITE_POLICY);+}+if(found_cache->flags&ACPI_PPTT_ALLOCATION_TYPE_VALID)+switch(found_cache->attributes&ACPI_PPTT_MASK_ALLOCATION_TYPE){+caseACPI_6_2_CACHE_READ_ALLOCATE:+this_leaf->attributes|=CACHE_READ_ALLOCATE;+break;+caseACPI_6_2_CACHE_WRITE_ALLOCATE:+this_leaf->attributes|=CACHE_WRITE_ALLOCATE;+break;+caseACPI_6_2_CACHE_RW_ALLOCATE:+this_leaf->attributes|=+CACHE_READ_ALLOCATE|CACHE_WRITE_ALLOCATE;+break;+default:+pr_err("Unknown ACPI cache allocation policy %d\n",+found_cache->attributes&ACPI_PPTT_MASK_ALLOCATION_TYPE);+}+}++staticvoidcache_setup_acpi_cpu(structacpi_table_header*table,+unsignedintcpu)+{+structacpi_pptt_cache*found_cache;+structcpu_cacheinfo*this_cpu_ci=get_cpu_cacheinfo(cpu);+u32acpi_cpu_id=acpi_cpu_get_madt_gicc(cpu)->uid;+structcacheinfo*this_leaf;+unsignedintindex=0;++while(index<get_cpu_cacheinfo(cpu)->num_leaves){+this_leaf=this_cpu_ci->info_list+index;+found_cache=acpi_find_cache_node(table,acpi_cpu_id,+this_leaf->type,+this_leaf->level);+pr_debug("found = %p\n",found_cache);+if(found_cache)+update_cache_properties(this_leaf,found_cache);++index++;+}+}++staticinttopology_setup_acpi_cpu(structacpi_table_header*table,+unsignedintcpu,intlevel)+{+structacpi_pptt_processor*cpu_node;+u32acpi_cpu_id=acpi_cpu_get_madt_gicc(cpu)->uid;++cpu_node=acpi_find_processor_node(table,acpi_cpu_id);+if(cpu_node){+cpu_node=acpi_find_processor_package_id(table,cpu_node,level);+return(int)((u8*)cpu_node-(u8*)table);+}+pr_err_once("PPTT table found, but unable to locate core for %d\n",+cpu);+return-ENOENT;+}++/*+*simplyassignaACPIcacheentrytoeachknownCPUcacheentry+*determiningwhichentriesaresharedisdonelater.+*/+intcache_setup_acpi(unsignedintcpu)+{+structacpi_table_header*table;+acpi_statusstatus;++pr_debug("Cache Setup ACPI cpu %d\n",cpu);++status=acpi_get_table(ACPI_SIG_PPTT,0,&table);+if(ACPI_FAILURE(status)){+pr_err_once("No PPTT table found, cache topology may be inaccurate\n");+return-ENOENT;+}++cache_setup_acpi_cpu(table,cpu);+acpi_put_table(table);++returnstatus;+}++/*+*DetermineatopologyuniqueIDforeachthread/core/cluster/socket/etc.+*ThisIDcanthenbeusedtogrouppeers.+*/+intsetup_acpi_cpu_topology(unsignedintcpu,intlevel)+{+structacpi_table_header*table;+acpi_statusstatus;+intretval;++status=acpi_get_table(ACPI_SIG_PPTT,0,&table);+if(ACPI_FAILURE(status)){+pr_err_once("No PPTT table found, cpu topology may be inaccurate\n");+return-ENOENT;+}+retval=topology_setup_acpi_cpu(table,cpu,level);+pr_debug("Topology Setup ACPI cpu %d, level %d ret = %d\n",+cpu,level,retval);+acpi_put_table(table);++returnretval;+}++/*+*WalkthePPTT,countthenumberofsocketswedetect+*/+intacpi_multisocket_count(void)+{+structacpi_table_header*table;+acpi_statusstatus;+intretval=0;++status=acpi_get_table(ACPI_SIG_PPTT,0,&table);+if(ACPI_FAILURE(status)){+pr_err_once("No PPTT table found, socket topology may be inaccurate\n");+return-ENOENT;+}+retval=acpi_count_socket_nodes(table);+acpi_put_table(table);++returnretval;+}
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:14
Now that we have a PPTT parser, in preparation for its use
on arm64, lets build it.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/Kconfig | 1 +
drivers/acpi/Makefile | 1 +
drivers/acpi/arm64/Kconfig | 3 +++
3 files changed, 5 insertions(+)
@@ -85,6 +85,7 @@ obj-$(CONFIG_ACPI_BGRT) += bgrt.oobj-$(CONFIG_ACPI_CPPC_LIB)+=cppc_acpi.oobj-$(CONFIG_ACPI_SPCR_TABLE)+=spcr.oobj-$(CONFIG_ACPI_DEBUGGER_USER)+=acpi_dbg.o+obj-$(CONFIG_ACPI_PPTT)+=pptt.o# processor has its own "processor." module_param namespaceprocessor-y:=processor_driver.o
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:15
The /sys cache entries should support ACPI/PPTT generated cache
topology information. Lets detect ACPI systems and call
an arch specific cache_setup_acpi() routine to update the hardware
probed cache topology.
For arm64, if ACPI is enabled, determine the max number of cache
levels and populate them using a PPTT table if one is available.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/cacheinfo.c | 23 ++++++++++++++++++-----
drivers/acpi/pptt.c | 1 +
drivers/base/cacheinfo.c | 17 +++++++++++------
include/linux/cacheinfo.h | 10 ++++++++--
4 files changed, 38 insertions(+), 13 deletions(-)
@@ -44,9 +45,17 @@ static void ci_leaf_init(struct cacheinfo *this_leaf,this_leaf->type=type;}+#ifndef CONFIG_ACPI+intacpi_find_last_cache_level(unsignedintcpu)+{+/*ACPI kernels should be built with PPTT support*/+return0;+}+#endif+staticint__init_cache_level(unsignedintcpu){-unsignedintctype,level,leaves,of_level;+unsignedintctype,level,leaves,fw_level;structcpu_cacheinfo*this_cpu_ci=get_cpu_cacheinfo(cpu);for(level=1,leaves=0;level<=MAX_CACHE_LEVEL;level++){
@@ -59,15 +68,19 @@ static int __init_cache_level(unsigned int cpu)leaves+=(ctype==CACHE_TYPE_SEPARATE)?2:1;}-of_level=of_find_last_cache_level(cpu);-if(level<of_level){+if(acpi_disabled)+fw_level=of_find_last_cache_level(cpu);+else+fw_level=acpi_find_last_cache_level(cpu);++if(level<fw_level){/**someexternalcachesnotspecifiedinCLIDR_EL1*theinformationmaybeavailableinthedevicetree*onlyunifiedexternalcachesareconsideredhere*/-leaves+=(of_level-level);-level=of_level;+leaves+=(fw_level-level);+level=fw_level;}this_cpu_ci->num_levels=level;
@@ -364,6 +364,7 @@ int acpi_find_last_cache_level(unsigned int cpu)staticvoidupdate_cache_properties(structcacheinfo*this_leaf,structacpi_pptt_cache*found_cache){+this_leaf->firmware_node=found_cache;if(found_cache->flags&ACPI_PPTT_SIZE_PROPERTY_VALID)this_leaf->size=found_cache->size;if(found_cache->flags&ACPI_PPTT_LINE_SIZE_VALID)
@@ -86,7 +86,7 @@ static int cache_setup_of_node(unsigned int cpu)staticinlineboolcache_leaves_are_shared(structcacheinfo*this_leaf,structcacheinfo*sib_leaf){-returnsib_leaf->of_node==this_leaf->of_node;+returnsib_leaf->firmware_node==this_leaf->firmware_node;}/* OF properties to query for a given cache type */
@@ -225,11 +230,11 @@ static int cache_shared_cpu_map_setup(unsigned int cpu)if(this_cpu_ci->cpu_map_populated)return0;-if(of_have_populated_dt())+if(!acpi_disabled)+ret=cache_setup_acpi(cpu);+elseif(of_have_populated_dt())ret=cache_setup_of_node(cpu);-elseif(!acpi_disabled)-/* No cache property/hierarchy support yet in ACPI */-ret=-ENOTSUPP;+if(ret)returnret;
@@ -286,7 +291,7 @@ static void cache_shared_cpu_map_remove(unsigned int cpu)staticvoidcache_override_properties(unsignedintcpu){-if(of_have_populated_dt())+if(acpi_disabled&&of_have_populated_dt())returncache_of_override_properties(cpu);}
@@ -98,6 +103,7 @@ int func(unsigned int cpu) \structcpu_cacheinfo*get_cpu_cacheinfo(unsignedintcpu);intinit_cache_level(unsignedintcpu);intpopulate_cache_leaves(unsignedintcpu);+intacpi_find_last_cache_level(unsignedintcpu);conststructattribute_group*cache_get_priv_group(structcacheinfo*this_leaf);
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:16
Many modern machines have cluster on die (COD) non-uniformity
as well as the traditional multi-socket architectures. Reusing
the multi-socket or NUMA on die concepts for these (as arm64 does)
breaks down when presented with actual multi-socket/COD machines.
Similar, problems are also visible on some x86 machines so it
seems appropriate to start abstracting and making these topologies
visible.
To start, a topology_cod_id() macro is added which defaults to returning
the same information as topology_physical_package_id(). Moving forward
we can start to spit out the differences.
For arm64, an additional package_id is added to the cpu_topology array.
Initially this will be equal to the cluster_id as well.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/include/asm/topology.h | 4 +++-
arch/arm64/kernel/topology.c | 8 ++++++--
include/linux/topology.h | 3 +++
3 files changed, 12 insertions(+), 3 deletions(-)
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:17
There are a few arm64 specific users (cpufreq, psci, etc) which really
want the cluster rather than the topology_physical_package_id(). Lets
convert those users to topology_cod_id(). That way when we start
differentiating the socket/cluster they will continue to behave correctly.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/clk/clk-mb86s7x.c | 2 +-
drivers/cpufreq/arm_big_little.c | 2 +-
drivers/firmware/psci_checker.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
From: Jeremy Linton <hidden> Date: 2017-09-14 18:49:18
Propagate the topology information from the PPTT tree to the
cpu_topology array. We can get the thread id, core_id and
cluster_id by assuming certain levels of the PPTT tree correspond
to those concepts. The package_id is flagged in the tree and can be
found by passing an arbitrary large level to setup_acpi_cpu_topology()
which terminates its search when it finds an ACPI node flagged
as the physical package. If the tree doesn't contain enough
levels to represent all of thread/core/cod/package then the package
id will be used for the missing levels.
Since arm64 machines can have 3 distinct topology levels, and the
scheduler only handles sockets/threads well today, we compromise
by collapsing into one of three diffrent configurations. These are
thread/socket, thread/cluster or cluster/socket depending on whether
the machine has threading and multisocket, threading in a single
socket, or doesn't have threading.
This code is loosely based on a combination of code from:
Xiongfeng Wang [off-list ref]
John Garry [off-list ref]
Jeffrey Hugo [off-list ref]
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/topology.c | 68 +++++++++++++++++++++++++++++++++++++++++++-
include/linux/topology.h | 2 ++
2 files changed, 69 insertions(+), 1 deletion(-)
@@ -304,6 +306,68 @@ static void __init reset_cpu_topology(void)}}+#ifdef CONFIG_ACPI+/*+*Propagatethetopologyinformationoftheprocessor_topology_nodetreetothe+*cpu_topologyarray.+*/+staticint__initparse_acpi_topology(void)+{+u64is_threaded;+intis_multisocket;+intcpu;+inttopology_id;+/* set a large depth, to hit ACPI_PPTT_PHYSICAL_PACKAGE if one exists */+constintmax_topo=0xFF;++is_threaded=read_cpuid_mpidr()&MPIDR_MT_BITMASK;+is_multisocket=acpi_multisocket_count();+if(is_multisocket<0)+returnis_multisocket;++for_each_possible_cpu(cpu){+topology_id=setup_acpi_cpu_topology(cpu,0);+if(topology_id<0)+returntopology_id;++if((is_threaded)&&(is_multisocket>1)){+/* MT per core, and multiple sockets */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,2);+cpu_topology[cpu].cluster_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,max_topo);+cpu_topology[cpu].package_id=topology_id;+}elseif(is_threaded){+/* mutltiple threads, but only a single socket */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,2);+cpu_topology[cpu].cluster_id=topology_id;+cpu_topology[cpu].package_id=topology_id;+}else{+/* no threads, clusters behave like threads */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+cpu_topology[cpu].cluster_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,max_topo);+cpu_topology[cpu].package_id=topology_id;+}+}+return0;+}++#else+staticint__initparse_acpi_topology(void)+{+/*ACPI kernels should be built with PPTT support*/+return-EINVAL;+}+#endif+void__initinit_cpu_topology(void){reset_cpu_topology();
From: Jeremy Linton <hidden> Date: 2017-09-15 17:05:06
On 09/14/2017 01:49 PM, Jeremy Linton wrote:
ACPI 6.2 adds the Processor Properties Topology Table (PPTT), which is
used to describe the processor and cache topologies. Ideally it is
used to extend/override information provided by the hardware, but
right now ARM64 is entirely dependent on firmware provided tables.
Hi,
So there is a problem with this patch set when cache nodes are
referenced by cpu nodes with the intention that the resulting caches
aren't shared, even though the PPTT cache node is shared. The code uses
the node reference when determining if caches are shared. This means
that it makes a mistake and thinks that all the (say L1) caches are
shared because they share a PPTT cache node.
Its a fairly small tweak, I will re-post this set.
This patch parses the table for the cache topology and CPU topology.
For the latter we also add an additional topology_cod_id() macro,
and a package_id for arm64. Initially the physical id will match
the cluster id, but we update users of the cluster to utilize
the new macro. When we enable PPTT for the arm64 the cluster/socket
starts to differ. Because of this we also make some dynamic decisions
about mapping thread/core/cod/socket to the thread/socket used by the
scheduler.
For example on juno:
[root at mammon-juno-rh topology]# lstopo-no-graphics
Machine (7048MB)
Package L#0
L2 L#0 (1024KB) + Core L#0
L1d L#0 (32KB) + L1i L#0 (32KB) + PU L#0 (P#0)
L1d L#1 (32KB) + L1i L#1 (32KB) + PU L#1 (P#1)
L1d L#2 (32KB) + L1i L#2 (32KB) + PU L#2 (P#2)
L1d L#3 (32KB) + L1i L#3 (32KB) + PU L#3 (P#3)
L2 L#1 (2048KB) + Core L#1
L1d L#4 (32KB) + L1i L#4 (48KB) + PU L#4 (P#4)
L1d L#5 (32KB) + L1i L#5 (48KB) + PU L#5 (P#5)
HostBridge L#0
PCIBridge
PCIBridge
PCIBridge
PCI 1095:3132
Block(Disk) L#0 "sda"
PCIBridge
PCI 1002:68f9
GPU L#1 "renderD128"
GPU L#2 "card0"
GPU L#3 "controlD64"
PCIBridge
PCI 11ab:4380
Net L#4 "enp8s0"
Jeremy Linton (6):
ACPI/PPTT: Add Processor Properties Topology Table parsing
ACPI: Enable PPTT support on ARM64
drivers: base: cacheinfo: arm64: Add support for ACPI based firmware
tables
Topology: Add cluster on die macros and arm64 decoding
arm64: Fixup users of topology_physical_package_id
arm64: topology: Enable ACPI/PPTT based CPU topology.
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/topology.h | 4 +-
arch/arm64/kernel/cacheinfo.c | 23 +-
arch/arm64/kernel/topology.c | 76 +++++-
drivers/acpi/Makefile | 1 +
drivers/acpi/arm64/Kconfig | 3 +
drivers/acpi/pptt.c | 508 ++++++++++++++++++++++++++++++++++++++
drivers/base/cacheinfo.c | 17 +-
drivers/clk/clk-mb86s7x.c | 2 +-
drivers/cpufreq/arm_big_little.c | 2 +-
drivers/firmware/psci_checker.c | 2 +-
include/linux/cacheinfo.h | 10 +-
include/linux/topology.h | 5 +
13 files changed, 634 insertions(+), 20 deletions(-)
create mode 100644 drivers/acpi/pptt.c
From: Xiongfeng Wang <hidden> Date: 2017-09-18 01:37:30
Hi Jeremy,
On 2017/9/15 2:49, Jeremy Linton wrote:
quoted hunk
Propagate the topology information from the PPTT tree to the
cpu_topology array. We can get the thread id, core_id and
cluster_id by assuming certain levels of the PPTT tree correspond
to those concepts. The package_id is flagged in the tree and can be
found by passing an arbitrary large level to setup_acpi_cpu_topology()
which terminates its search when it finds an ACPI node flagged
as the physical package. If the tree doesn't contain enough
levels to represent all of thread/core/cod/package then the package
id will be used for the missing levels.
Since arm64 machines can have 3 distinct topology levels, and the
scheduler only handles sockets/threads well today, we compromise
by collapsing into one of three diffrent configurations. These are
thread/socket, thread/cluster or cluster/socket depending on whether
the machine has threading and multisocket, threading in a single
socket, or doesn't have threading.
This code is loosely based on a combination of code from:
Xiongfeng Wang [off-list ref]
John Garry [off-list ref]
Jeffrey Hugo [off-list ref]
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/topology.c | 68 +++++++++++++++++++++++++++++++++++++++++++-
include/linux/topology.h | 2 ++
2 files changed, 69 insertions(+), 1 deletion(-)
@@ -304,6 +306,68 @@ static void __init reset_cpu_topology(void)}}+#ifdef CONFIG_ACPI+/*+*Propagatethetopologyinformationoftheprocessor_topology_nodetreetothe+*cpu_topologyarray.+*/+staticint__initparse_acpi_topology(void)+{+u64is_threaded;+intis_multisocket;+intcpu;+inttopology_id;+/* set a large depth, to hit ACPI_PPTT_PHYSICAL_PACKAGE if one exists */+constintmax_topo=0xFF;++is_threaded=read_cpuid_mpidr()&MPIDR_MT_BITMASK;+is_multisocket=acpi_multisocket_count();+if(is_multisocket<0)+returnis_multisocket;++for_each_possible_cpu(cpu){+topology_id=setup_acpi_cpu_topology(cpu,0);+if(topology_id<0)+returntopology_id;++if((is_threaded)&&(is_multisocket>1)){+/* MT per core, and multiple sockets */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,2);+cpu_topology[cpu].cluster_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,max_topo);+cpu_topology[cpu].package_id=topology_id;+}elseif(is_threaded){+/* mutltiple threads, but only a single socket */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,2);+cpu_topology[cpu].cluster_id=topology_id;+cpu_topology[cpu].package_id=topology_id;+}else{+/* no threads, clusters behave like threads */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+cpu_topology[cpu].cluster_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,max_topo);+cpu_topology[cpu].package_id=topology_id;
I can not understand why should we consider cores in a cluster as threads. The scheduler will
be effected a lot by this. And the 'lstopo' may display wrong information.
Thanks,
Xiongfeng Wang
quoted hunk
+ }
+ }
+ return 0;
+}
+
+#else
+static int __init parse_acpi_topology(void)
+{
+ /*ACPI kernels should be built with PPTT support*/
+ return -EINVAL;
+}
+#endif
+
void __init init_cpu_topology(void)
{
reset_cpu_topology();
@@ -312,6 +376,8 @@ void __init init_cpu_topology(void) * Discard anything that was parsed if we hit an error so we * don't use partial information. */- if (of_have_populated_dt() && parse_dt_topology())+ if ((!acpi_disabled) && parse_acpi_topology())+ reset_cpu_topology();+ else if (of_have_populated_dt() && parse_dt_topology()) reset_cpu_topology(); }
From: Xiongfeng Wang <hidden> Date: 2017-09-18 01:50:27
Hi Jeremy,
On 2017/9/15 2:49, Jeremy Linton wrote:
quoted hunk
Many modern machines have cluster on die (COD) non-uniformity
as well as the traditional multi-socket architectures. Reusing
the multi-socket or NUMA on die concepts for these (as arm64 does)
breaks down when presented with actual multi-socket/COD machines.
Similar, problems are also visible on some x86 machines so it
seems appropriate to start abstracting and making these topologies
visible.
To start, a topology_cod_id() macro is added which defaults to returning
the same information as topology_physical_package_id(). Moving forward
we can start to spit out the differences.
For arm64, an additional package_id is added to the cpu_topology array.
Initially this will be equal to the cluster_id as well.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/include/asm/topology.h | 4 +++-
arch/arm64/kernel/topology.c | 8 ++++++--
include/linux/topology.h | 3 +++
3 files changed, 12 insertions(+), 3 deletions(-)
'core_sibling' will be updated by 'update_siblings_masks()' to represent cores in a cluster;
Can we add a cpumask_t field to represent cores in a package? So that 'lstopo' can use this
cpumask_t to display the right information.
Thanks,
Xiongfeng Wang
From: Jeremy Linton <hidden> Date: 2017-09-18 18:54:26
Hi,
On 09/17/2017 08:50 PM, Xiongfeng Wang wrote:
Hi Jeremy,
On 2017/9/15 2:49, Jeremy Linton wrote:
quoted
Many modern machines have cluster on die (COD) non-uniformity
as well as the traditional multi-socket architectures. Reusing
the multi-socket or NUMA on die concepts for these (as arm64 does)
breaks down when presented with actual multi-socket/COD machines.
Similar, problems are also visible on some x86 machines so it
seems appropriate to start abstracting and making these topologies
visible.
To start, a topology_cod_id() macro is added which defaults to returning
the same information as topology_physical_package_id(). Moving forward
we can start to spit out the differences.
For arm64, an additional package_id is added to the cpu_topology array.
Initially this will be equal to the cluster_id as well.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/include/asm/topology.h | 4 +++-
arch/arm64/kernel/topology.c | 8 ++++++--
include/linux/topology.h | 3 +++
3 files changed, 12 insertions(+), 3 deletions(-)
'core_sibling' will be updated by 'update_siblings_masks()' to represent cores in a cluster;
Can we add a cpumask_t field to represent cores in a package? So that 'lstopo' can use this
cpumask_t to display the right information.
So, the change below modifies update_siblings_mask() to utilize the
package_id. Per the ABI the ..cpuX/topology/physical_package_id is
shared between the core_siblings/core_siblings_list. What
physical_package_id means can vary per architecture, but the siblings
list needs to be the cores with the same phyiscal_package (AFAIK, feel
free to correct my understanding). That rule should be enforced by this
patch set.
I suspect if your running these patches, and the lstopo output looks
strange its because your on a machine where the thread_id has been
assigned the cluster_id in the later patch set.
From: Jeremy Linton <hidden> Date: 2017-09-18 19:02:00
On 09/17/2017 08:37 PM, Xiongfeng Wang wrote:
Hi Jeremy,
On 2017/9/15 2:49, Jeremy Linton wrote:
quoted
Propagate the topology information from the PPTT tree to the
cpu_topology array. We can get the thread id, core_id and
cluster_id by assuming certain levels of the PPTT tree correspond
to those concepts. The package_id is flagged in the tree and can be
found by passing an arbitrary large level to setup_acpi_cpu_topology()
which terminates its search when it finds an ACPI node flagged
as the physical package. If the tree doesn't contain enough
levels to represent all of thread/core/cod/package then the package
id will be used for the missing levels.
Since arm64 machines can have 3 distinct topology levels, and the
scheduler only handles sockets/threads well today, we compromise
by collapsing into one of three diffrent configurations. These are
thread/socket, thread/cluster or cluster/socket depending on whether
the machine has threading and multisocket, threading in a single
socket, or doesn't have threading.
This code is loosely based on a combination of code from:
Xiongfeng Wang [off-list ref]
John Garry [off-list ref]
Jeffrey Hugo [off-list ref]
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/topology.c | 68 +++++++++++++++++++++++++++++++++++++++++++-
include/linux/topology.h | 2 ++
2 files changed, 69 insertions(+), 1 deletion(-)
@@ -304,6 +306,68 @@ static void __init reset_cpu_topology(void)}}+#ifdef CONFIG_ACPI+/*+*Propagatethetopologyinformationoftheprocessor_topology_nodetreetothe+*cpu_topologyarray.+*/+staticint__initparse_acpi_topology(void)+{+u64is_threaded;+intis_multisocket;+intcpu;+inttopology_id;+/* set a large depth, to hit ACPI_PPTT_PHYSICAL_PACKAGE if one exists */+constintmax_topo=0xFF;++is_threaded=read_cpuid_mpidr()&MPIDR_MT_BITMASK;+is_multisocket=acpi_multisocket_count();+if(is_multisocket<0)+returnis_multisocket;++for_each_possible_cpu(cpu){+topology_id=setup_acpi_cpu_topology(cpu,0);+if(topology_id<0)+returntopology_id;++if((is_threaded)&&(is_multisocket>1)){+/* MT per core, and multiple sockets */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,2);+cpu_topology[cpu].cluster_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,max_topo);+cpu_topology[cpu].package_id=topology_id;+}elseif(is_threaded){+/* mutltiple threads, but only a single socket */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,2);+cpu_topology[cpu].cluster_id=topology_id;+cpu_topology[cpu].package_id=topology_id;+}else{+/* no threads, clusters behave like threads */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+cpu_topology[cpu].cluster_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,max_topo);+cpu_topology[cpu].package_id=topology_id;
I can not understand why should we consider cores in a cluster as threads. The scheduler will
be effected a lot by this. And the 'lstopo' may display wrong information.
My take, is that we shouldn't be discarding the cluster information
because its extremely valuable. In many ways it seems that clustered
cores have, at a high level, similar performance characteristics to
threads (AKA, cores in a cluster have high performance when sharing
data, but for problems with little sharing its more advantageous to
first schedule those threads to differing clusters). Although, how much
affect this has vs the MC cache priorities in the scheduler isn't
apparent to me.
Anyway, lstopo doesn't currently know about anything beyond
package/thread, except for the book. The question is, do we want to
misuse the book_id to represent sockets and continue to use cluster_id
as the physical_package_id? I don't think that is a better plan than
what I've done here.
The bottom line, is that after having looked at the scheduler a bit, I
suspect that thread=cluster for machines without MT doesn't' really
matter much. So, the next version i'm just going to collapse this into
what everyone expects socket=socket and thread=thread for ACPI users
(which are more likely to have NUMA and multisocket at this point). The
cluster knowledge is still somewhat visible to the scheduler via the
cache topology.
Thanks,
Xiongfeng Wang
quoted
+ }
+ }
+ return 0;
+}
+
+#else
+static int __init parse_acpi_topology(void)
+{
+ /*ACPI kernels should be built with PPTT support*/
+ return -EINVAL;
+}
+#endif
+
void __init init_cpu_topology(void)
{
reset_cpu_topology();
@@ -312,6 +376,8 @@ void __init init_cpu_topology(void) * Discard anything that was parsed if we hit an error so we * don't use partial information. */- if (of_have_populated_dt() && parse_dt_topology())+ if ((!acpi_disabled) && parse_acpi_topology())+ reset_cpu_topology();+ else if (of_have_populated_dt() && parse_dt_topology()) reset_cpu_topology(); }
From: Xiongfeng Wang <hidden> Date: 2017-09-19 01:03:47
Hi Jeremy,
On 2017/9/19 2:54, Jeremy Linton wrote:
Hi,
On 09/17/2017 08:50 PM, Xiongfeng Wang wrote:
quoted
Hi Jeremy,
On 2017/9/15 2:49, Jeremy Linton wrote:
quoted
Many modern machines have cluster on die (COD) non-uniformity
as well as the traditional multi-socket architectures. Reusing
the multi-socket or NUMA on die concepts for these (as arm64 does)
breaks down when presented with actual multi-socket/COD machines.
Similar, problems are also visible on some x86 machines so it
seems appropriate to start abstracting and making these topologies
visible.
To start, a topology_cod_id() macro is added which defaults to returning
the same information as topology_physical_package_id(). Moving forward
we can start to spit out the differences.
For arm64, an additional package_id is added to the cpu_topology array.
Initially this will be equal to the cluster_id as well.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/include/asm/topology.h | 4 +++-
arch/arm64/kernel/topology.c | 8 ++++++--
include/linux/topology.h | 3 +++
3 files changed, 12 insertions(+), 3 deletions(-)
'core_sibling' will be updated by 'update_siblings_masks()' to represent cores in a cluster;
Can we add a cpumask_t field to represent cores in a package? So that 'lstopo' can use this
cpumask_t to display the right information.
So, the change below modifies update_siblings_mask() to utilize the package_id. Per the ABI the ..cpuX/topology/physical_package_id is shared between the core_siblings/core_siblings_list. What physical_package_id means can vary per architecture, but the siblings list needs to be the cores with the same phyiscal_package (AFAIK, feel free to correct my understanding). That rule should be enforced by this patch set.
I suspect if your running these patches, and the lstopo output looks strange its because your on a machine where the thread_id has been assigned the cluster_id in the later patch set.
Sorry, I didn't notice your change in 'update_siblings_masks()' before, so 'core_sibling' are represent cores in a package now.
But we may need another cpumask_t field to represent cores in a cluster, so that the scheduler can use it to build a sched_domain
only with cores in one cluster.
From: Xiongfeng Wang <hidden> Date: 2017-09-19 01:41:01
Hi Jeremy,
On 2017/9/19 3:02, Jeremy Linton wrote:
On 09/17/2017 08:37 PM, Xiongfeng Wang wrote:
quoted
Hi Jeremy,
On 2017/9/15 2:49, Jeremy Linton wrote:
quoted
Propagate the topology information from the PPTT tree to the
cpu_topology array. We can get the thread id, core_id and
cluster_id by assuming certain levels of the PPTT tree correspond
to those concepts. The package_id is flagged in the tree and can be
found by passing an arbitrary large level to setup_acpi_cpu_topology()
which terminates its search when it finds an ACPI node flagged
as the physical package. If the tree doesn't contain enough
levels to represent all of thread/core/cod/package then the package
id will be used for the missing levels.
Since arm64 machines can have 3 distinct topology levels, and the
scheduler only handles sockets/threads well today, we compromise
by collapsing into one of three diffrent configurations. These are
thread/socket, thread/cluster or cluster/socket depending on whether
the machine has threading and multisocket, threading in a single
socket, or doesn't have threading.
This code is loosely based on a combination of code from:
Xiongfeng Wang [off-list ref]
John Garry [off-list ref]
Jeffrey Hugo [off-list ref]
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/topology.c | 68 +++++++++++++++++++++++++++++++++++++++++++-
include/linux/topology.h | 2 ++
2 files changed, 69 insertions(+), 1 deletion(-)
@@ -304,6 +306,68 @@ static void __init reset_cpu_topology(void)}}+#ifdefCONFIG_ACPI+/*+*Propagatethetopologyinformationoftheprocessor_topology_nodetreetothe+*cpu_topologyarray.+*/+staticint__initparse_acpi_topology(void)+{+u64is_threaded;+intis_multisocket;+intcpu;+inttopology_id;+/* set a large depth, to hit ACPI_PPTT_PHYSICAL_PACKAGE if one exists */+constintmax_topo=0xFF;++is_threaded=read_cpuid_mpidr()&MPIDR_MT_BITMASK;+is_multisocket=acpi_multisocket_count();+if(is_multisocket<0)+returnis_multisocket;++for_each_possible_cpu(cpu){+topology_id=setup_acpi_cpu_topology(cpu,0);+if(topology_id<0)+returntopology_id;++if((is_threaded)&&(is_multisocket>1)){+/* MT per core, and multiple sockets */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,2);+cpu_topology[cpu].cluster_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,max_topo);+cpu_topology[cpu].package_id=topology_id;+}elseif(is_threaded){+/* mutltiple threads, but only a single socket */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,2);+cpu_topology[cpu].cluster_id=topology_id;+cpu_topology[cpu].package_id=topology_id;+}else{+/* no threads, clusters behave like threads */+cpu_topology[cpu].thread_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,1);+cpu_topology[cpu].core_id=topology_id;+cpu_topology[cpu].cluster_id=topology_id;+topology_id=setup_acpi_cpu_topology(cpu,max_topo);+cpu_topology[cpu].package_id=topology_id;
I can not understand why should we consider cores in a cluster as threads. The scheduler will
be effected a lot by this. And the 'lstopo' may display wrong information.
My take, is that we shouldn't be discarding the cluster information because its extremely valuable. In many ways it seems that clustered cores have, at a high level,
similar performance characteristics to threads (AKA, cores in a cluster have high performance when sharing data, but for problems with little sharing its more advantageous to
first schedule those threads to differing clusters). Although, how much affect this has vs the MC cache priorities in the scheduler isn't apparent to me.
The code for sched_domain building for arm64 is as below. 'cpu_smt_mask' use 'thread_sibling' in struct cpu_topology, and 'cpu_coregroup_mask' use 'core_sibling' in struct cpu_topology.
But the defconfig for ARM64 does not include 'CONFIG_SCHED_SMT'. If we add a *_sibling field in struct cpu_topology to represent cores in one cluster, and change 'cpu_coregroup_mask'
to use this field, we can build a sched_domain only with cores in a cluster.
static struct sched_domain_topology_level default_topology[] = {
#ifdef CONFIG_SCHED_SMT
{ cpu_smt_mask, cpu_smt_flags, SD_INIT_NAME(SMT) },
#endif
#ifdef CONFIG_SCHED_MC
{ cpu_coregroup_mask, cpu_core_flags, SD_INIT_NAME(MC) },
#endif
{ cpu_cpu_mask, SD_INIT_NAME(DIE) },
{ NULL, },
};
Anyway, lstopo doesn't currently know about anything beyond package/thread, except for the book. The question is, do we want to misuse the book_id to represent sockets and
continue to use cluster_id as the physical_package_id? I don't think that is a better plan than what I've done here.
Sorry I didn't know much about the book_id. For my understanding, 'lstopo' use the information from the sysfs. So I search the linux code for 'book_id' and found out that
'book_id' seems to be used in S390 architecture only.
The bottom line, is that after having looked at the scheduler a bit, I suspect that thread=cluster for machines without MT doesn't' really matter much. So, the next version
i'm just going to collapse this into what everyone expects socket=socket and thread=thread for ACPI users (which are more likely to have NUMA and multisocket at this point). The
cluster knowledge is still somewhat visible to the scheduler via the cache topology.
quoted
Thanks,
Xiongfeng Wang
quoted
+ }
+ }
+ return 0;
+}
+
+#else
+static int __init parse_acpi_topology(void)
+{
+ /*ACPI kernels should be built with PPTT support*/
+ return -EINVAL;
+}
+#endif
+
void __init init_cpu_topology(void)
{
reset_cpu_topology();
@@ -312,6 +376,8 @@ void __init init_cpu_topology(void) * Discard anything that was parsed if we hit an error so we * don't use partial information. */- if (of_have_populated_dt() && parse_dt_topology())+ if ((!acpi_disabled) && parse_acpi_topology())+ reset_cpu_topology();+ else if (of_have_populated_dt() && parse_dt_topology()) reset_cpu_topology(); }