// SPDX-License-Identifier: GPL-2.0
/*
* pptt . c - parsing of Processor Properties Topology Table ( PPTT )
*
* Copyright ( C ) 2018 , ARM
*
* This file implements parsing of the Processor Properties Topology Table
* which is optionally used to describe the processor and cache topology .
* Due to the relative pointers used throughout the table , this doesn ' t
* leverage the existing subtable parsing in the kernel .
*
* The PPTT structure is an inverted tree , with each node potentially
* holding one or two inverted tree data structures describing
* the caches available at that level . Each cache structure optionally
* contains properties describing the cache at a given level which can be
* used to override hardware probed values .
*/
#define pr_fmt(fmt) "ACPI PPTT: " fmt
#include <linux/acpi.h>
#include <linux/cacheinfo.h>
#include <acpi/processor.h>
static struct acpi_subtable_header *fetch_pptt_subtable(struct acpi_table_header *table_hdr,
u32 pptt_ref)
{
struct acpi_subtable_header *entry;
/* there isn't a subtable at reference 0 */
if (pptt_ref < sizeof (struct acpi_subtable_header))
return NULL;
if (pptt_ref + sizeof (struct acpi_subtable_header) > table_hdr->length)
return NULL;
entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, pptt_ref);
if (entry->length == 0 )
return NULL;
if (pptt_ref + entry->length > table_hdr->length)
return NULL;
return entry;
}
static struct acpi_pptt_processor *fetch_pptt_node(struct acpi_table_header *table_hdr,
u32 pptt_ref)
{
return (struct acpi_pptt_processor *)fetch_pptt_subtable(table_hdr, pptt_ref);
}
static struct acpi_pptt_cache *fetch_pptt_cache(struct acpi_table_header *table_hdr,
u32 pptt_ref)
{
return (struct acpi_pptt_cache *)fetch_pptt_subtable(table_hdr, pptt_ref);
}
static struct acpi_subtable_header *acpi_get_pptt_resource(struct acpi_table_header *table_hdr,
struct acpi_pptt_processor *node,
int resource)
{
u32 *ref;
if (resource >= node->number_of_priv_resources)
return NULL;
ref = ACPI_ADD_PTR(u32, node, sizeof (struct acpi_pptt_processor));
ref += resource;
return fetch_pptt_subtable(table_hdr, *ref);
}
static inline bool acpi_pptt_match_type(int table_type, int type)
{
return ((table_type & ACPI_PPTT_MASK_CACHE_TYPE) == type ||
table_type & ACPI_PPTT_CACHE_TYPE_UNIFIED & type);
}
/**
* acpi_pptt_walk_cache ( ) - Attempt to find the requested acpi_pptt_cache
* @ table_hdr : Pointer to the head of the PPTT table
* @ local_level : passed res reflects this cache level
* @ split_levels : Number of split cache levels ( data / instruction ) .
* @ res : cache resource in the PPTT we want to walk
* @ found : returns a pointer to the requested level if found
* @ level : the requested cache level
* @ type : the requested cache type
*
* Attempt to find a given cache level , while counting the max number
* of cache levels for the cache node .
*
* Given a pptt resource , verify that it is a cache node , then walk
* down each level of caches , counting how many levels are found
* as well as checking the cache type ( icache , dcache , unified ) . If a
* level & type match , then we set found , and continue the search .
* Once the entire cache branch has been walked return its max
* depth .
*
* Return : The cache structure and the level we terminated with .
*/
static unsigned int acpi_pptt_walk_cache(struct acpi_table_header *table_hdr,
unsigned int local_level,
unsigned int *split_levels,
struct acpi_subtable_header *res,
struct acpi_pptt_cache **found,
unsigned int level, int type)
{
struct acpi_pptt_cache *cache;
if (res->type != ACPI_PPTT_TYPE_CACHE)
return 0 ;
cache = (struct acpi_pptt_cache *) res;
while (cache) {
local_level++;
if (!(cache->flags & ACPI_PPTT_CACHE_TYPE_VALID)) {
cache = fetch_pptt_cache(table_hdr, cache->next_level_of_cache);
continue ;
}
if (split_levels &&
(acpi_pptt_match_type(cache->attributes, ACPI_PPTT_CACHE_TYPE_DATA) ||
acpi_pptt_match_type(cache->attributes, ACPI_PPTT_CACHE_TYPE_INSTR)))
*split_levels = local_level;
if (local_level == level &&
acpi_pptt_match_type(cache->attributes, type)) {
if (*found != NULL && cache != *found)
pr_warn("Found duplicate cache level/type unable to determine uniqueness\n" );
pr_debug("Found cache @ level %u\n" , level);
*found = cache;
/*
* continue looking at this node ' s resource list
* to verify that we don ' t find a duplicate
* cache node .
*/
}
cache = fetch_pptt_cache(table_hdr, cache->next_level_of_cache);
}
return local_level;
}
static struct acpi_pptt_cache *
acpi_find_cache_level(struct acpi_table_header *table_hdr,
struct acpi_pptt_processor *cpu_node,
unsigned int *starting_level, unsigned int *split_levels,
unsigned int level, int type)
{
struct acpi_subtable_header *res;
unsigned int number_of_levels = *starting_level;
int resource = 0 ;
struct acpi_pptt_cache *ret = NULL;
unsigned int local_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,
split_levels, res, &ret,
level, type);
/*
* we are looking for the max depth . Since its potentially
* possible for a given node to have resources with differing
* depths verify that the depth we have found is the largest .
*/
if (number_of_levels < local_level)
number_of_levels = local_level;
}
if (number_of_levels > *starting_level)
*starting_level = number_of_levels;
return ret;
}
/**
* acpi_count_levels ( ) - Given a PPTT table , and a CPU node , count the cache
* levels and split cache levels ( data / instruction ) .
* @ table_hdr : Pointer to the head of the PPTT table
* @ cpu_node : processor node we wish to count caches for
* @ levels : Number of levels if success .
* @ split_levels : Number of split cache levels ( data / instruction ) if
* success . Can by NULL .
*
* Given a processor node containing a processing unit , walk into it and count
* how many levels exist solely for it , and then walk up each level until we hit
* the root node ( ignore the package level because it may be possible to have
* caches that exist across packages ) . Count the number of cache levels and
* split cache levels ( data / instruction ) that exist at each level on the way
* up .
*/
static void acpi_count_levels(struct acpi_table_header *table_hdr,
struct acpi_pptt_processor *cpu_node,
unsigned int *levels, unsigned int *split_levels)
{
do {
acpi_find_cache_level(table_hdr, cpu_node, levels, split_levels, 0 , 0 );
cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent);
} while (cpu_node);
}
/**
* acpi_pptt_leaf_node ( ) - Given a processor node , determine if its a leaf
* @ table_hdr : Pointer to the head of the PPTT table
* @ node : passed node is checked to see if its a leaf
*
* Determine if the * node parameter is a leaf node by iterating the
* PPTT table , looking for nodes which reference it .
*
* Return : 0 if we find a node referencing the passed node ( or table error ) ,
* or 1 if we don ' t .
*/
static int acpi_pptt_leaf_node(struct acpi_table_header *table_hdr,
struct acpi_pptt_processor *node)
{
struct acpi_subtable_header *entry;
unsigned long table_end;
u32 node_entry;
struct acpi_pptt_processor *cpu_node;
u32 proc_sz;
if (table_hdr->revision > 1 )
return (node->flags & ACPI_PPTT_ACPI_LEAF_NODE);
table_end = (unsigned long )table_hdr + table_hdr->length;
node_entry = ACPI_PTR_DIFF(node, table_hdr);
entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr,
sizeof (struct acpi_table_pptt));
proc_sz = sizeof (struct acpi_pptt_processor);
/* ignore subtable types that are smaller than a processor node */
while ((unsigned long )entry + proc_sz <= table_end) {
cpu_node = (struct acpi_pptt_processor *)entry;
if (entry->type == ACPI_PPTT_TYPE_PROCESSOR &&
cpu_node->parent == node_entry)
return 0 ;
if (entry->length == 0 )
return 0 ;
entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry,
entry->length);
}
return 1 ;
}
/**
* acpi_find_processor_node ( ) - Given a PPTT table find the requested processor
* @ table_hdr : Pointer to the head of the PPTT table
* @ acpi_cpu_id : CPU we are searching for
*
* Find the subtable entry describing the provided processor .
* This is done by iterating the PPTT table looking for processor nodes
* which have an acpi_processor_id that matches the acpi_cpu_id parameter
* passed into the function . If we find a node that matches this criteria
* we verify that its a leaf node in the topology rather than depending
* on the valid flag , which doesn ' t need to be set for leaf nodes .
*
* Return : NULL , or the processors acpi_pptt_processor *
*/
static struct acpi_pptt_processor *acpi_find_processor_node(struct acpi_table_header *table_hdr,
u32 acpi_cpu_id)
{
struct acpi_subtable_header *entry;
unsigned long table_end;
struct acpi_pptt_processor *cpu_node;
u32 proc_sz;
table_end = (unsigned long )table_hdr + table_hdr->length;
entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr,
sizeof (struct acpi_table_pptt));
proc_sz = sizeof (struct acpi_pptt_processor);
/* find the processor structure associated with this cpuid */
while ((unsigned long )entry + proc_sz <= table_end) {
cpu_node = (struct acpi_pptt_processor *)entry;
if (entry->length == 0 ) {
pr_warn("Invalid zero length subtable\n" );
break ;
}
/* entry->length may not equal proc_sz, revalidate the processor structure length */
if (entry->type == ACPI_PPTT_TYPE_PROCESSOR &&
acpi_cpu_id == cpu_node->acpi_processor_id &&
(unsigned long )entry + entry->length <= table_end &&
entry->length == proc_sz + cpu_node->number_of_priv_resources * sizeof (u32) &&
acpi_pptt_leaf_node(table_hdr, cpu_node)) {
return (struct acpi_pptt_processor *)entry;
}
entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry,
entry->length);
}
return NULL;
}
static u8 acpi_cache_type(enum cache_type type)
{
switch (type) {
case CACHE_TYPE_DATA:
pr_debug("Looking for data cache\n" );
return ACPI_PPTT_CACHE_TYPE_DATA;
case CACHE_TYPE_INST:
pr_debug("Looking for instruction cache\n" );
return ACPI_PPTT_CACHE_TYPE_INSTR;
default :
case CACHE_TYPE_UNIFIED:
pr_debug("Looking for unified cache\n" );
/*
* It is important that ACPI_PPTT_CACHE_TYPE_UNIFIED
* contains the bit pattern that will match both
* ACPI unified bit patterns because we use it later
* to match both cases .
*/
return ACPI_PPTT_CACHE_TYPE_UNIFIED;
}
}
static struct acpi_pptt_cache *acpi_find_cache_node(struct acpi_table_header *table_hdr,
u32 acpi_cpu_id,
enum cache_type type,
unsigned int level,
struct acpi_pptt_processor **node)
{
unsigned int total_levels = 0 ;
struct acpi_pptt_cache *found = NULL;
struct acpi_pptt_processor *cpu_node;
u8 acpi_type = acpi_cache_type(type);
pr_debug("Looking for CPU %d's level %u cache type %d\n" ,
acpi_cpu_id, level, acpi_type);
cpu_node = acpi_find_processor_node(table_hdr, acpi_cpu_id);
while (cpu_node && !found) {
found = acpi_find_cache_level(table_hdr, cpu_node,
&total_levels, NULL, level, acpi_type);
*node = cpu_node;
cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent);
}
return found;
}
/**
* update_cache_properties ( ) - Update cacheinfo for the given processor
* @ this_leaf : Kernel cache info structure being updated
* @ found_cache : The PPTT node describing this cache instance
* @ cpu_node : A unique reference to describe this cache instance
* @ revision : The revision of the PPTT table
*
* The ACPI spec implies that the fields in the cache structures are used to
* extend and correct the information probed from the hardware . Lets only
* set fields that we determine are VALID .
*
* Return : nothing . Side effect of updating the global cacheinfo
*/
static void update_cache_properties(struct cacheinfo *this_leaf,
struct acpi_pptt_cache *found_cache,
struct acpi_pptt_processor *cpu_node,
u8 revision)
{
struct acpi_pptt_cache_v1* found_cache_v1;
this_leaf->fw_token = cpu_node;
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) {
case ACPI_PPTT_CACHE_POLICY_WT:
this_leaf->attributes = CACHE_WRITE_THROUGH;
break ;
case ACPI_PPTT_CACHE_POLICY_WB:
this_leaf->attributes = CACHE_WRITE_BACK;
break ;
}
}
if (found_cache->flags & ACPI_PPTT_ALLOCATION_TYPE_VALID) {
switch (found_cache->attributes & ACPI_PPTT_MASK_ALLOCATION_TYPE) {
case ACPI_PPTT_CACHE_READ_ALLOCATE:
this_leaf->attributes |= CACHE_READ_ALLOCATE;
break ;
case ACPI_PPTT_CACHE_WRITE_ALLOCATE:
this_leaf->attributes |= CACHE_WRITE_ALLOCATE;
break ;
case ACPI_PPTT_CACHE_RW_ALLOCATE:
case ACPI_PPTT_CACHE_RW_ALLOCATE_ALT:
this_leaf->attributes |=
CACHE_READ_ALLOCATE | CACHE_WRITE_ALLOCATE;
break ;
}
}
/*
* If cache type is NOCACHE , then the cache hasn ' t been specified
* via other mechanisms . Update the type if a cache type has been
* provided .
*
* Note , we assume such caches are unified based on conventional system
* design and known examples . Significant work is required elsewhere to
* fully support data / instruction only type caches which are only
* specified in PPTT .
*/
if (this_leaf->type == CACHE_TYPE_NOCACHE &&
found_cache->flags & ACPI_PPTT_CACHE_TYPE_VALID)
this_leaf->type = CACHE_TYPE_UNIFIED;
if (revision >= 3 && (found_cache->flags & ACPI_PPTT_CACHE_ID_VALID)) {
found_cache_v1 = ACPI_ADD_PTR(struct acpi_pptt_cache_v1,
found_cache, sizeof (struct acpi_pptt_cache));
this_leaf->id = found_cache_v1->cache_id;
this_leaf->attributes |= CACHE_ID;
}
}
static void cache_setup_acpi_cpu(struct acpi_table_header *table,
unsigned int cpu)
{
struct acpi_pptt_cache *found_cache;
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
struct cacheinfo *this_leaf;
unsigned int index = 0 ;
struct acpi_pptt_processor *cpu_node = NULL;
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,
&cpu_node);
pr_debug("found = %p %p\n" , found_cache, cpu_node);
if (found_cache)
update_cache_properties(this_leaf, found_cache,
ACPI_TO_POINTER(ACPI_PTR_DIFF(cpu_node, table)),
table->revision);
index++;
}
}
static bool flag_identical(struct acpi_table_header *table_hdr,
struct acpi_pptt_processor *cpu)
{
struct acpi_pptt_processor *next;
/* heterogeneous machines must use PPTT revision > 1 */
if (table_hdr->revision < 2 )
return false ;
/* Locate the last node in the tree with IDENTICAL set */
if (cpu->flags & ACPI_PPTT_ACPI_IDENTICAL) {
next = fetch_pptt_node(table_hdr, cpu->parent);
if (!(next && next->flags & ACPI_PPTT_ACPI_IDENTICAL))
return true ;
}
return false ;
}
/* Passing level values greater than this will result in search termination */
#define PPTT_ABORT_PACKAGE 0 xFF
static struct acpi_pptt_processor *acpi_find_processor_tag(struct acpi_table_header *table_hdr,
struct acpi_pptt_processor *cpu,
int level, int flag)
{
struct acpi_pptt_processor *prev_node;
while (cpu && level) {
/* special case the identical flag to find last identical */
if (flag == ACPI_PPTT_ACPI_IDENTICAL) {
if (flag_identical(table_hdr, cpu))
break ;
} else if (cpu->flags & flag)
break ;
pr_debug("level %d\n" , level);
prev_node = fetch_pptt_node(table_hdr, cpu->parent);
if (prev_node == NULL)
break ;
cpu = prev_node;
level--;
}
return cpu;
}
static void acpi_pptt_warn_missing(void )
{
pr_warn_once("No PPTT table found, CPU and cache topology may be inaccurate\n" );
}
/**
* topology_get_acpi_cpu_tag ( ) - Find a unique topology value for a feature
* @ table : Pointer to the head of the PPTT table
* @ cpu : Kernel logical CPU number
* @ level : A level that terminates the search
* @ flag : A flag which terminates the search
*
* Get a unique value given a CPU , and a topology level , that can be
* matched to determine which cpus share common topological features
* at that level .
*
* Return : Unique value , or - ENOENT if unable to locate CPU
*/
static int topology_get_acpi_cpu_tag(struct acpi_table_header *table,
unsigned int cpu, int level, int flag)
{
struct acpi_pptt_processor *cpu_node;
u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
if (cpu_node) {
cpu_node = acpi_find_processor_tag(table, cpu_node,
level, flag);
/*
* As per specification if the processor structure represents
* an actual processor , then ACPI processor ID must be valid .
* For processor containers ACPI_PPTT_ACPI_PROCESSOR_ID_VALID
* should be set if the UID is valid
*/
if (level == 0 ||
cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID)
return cpu_node->acpi_processor_id;
return ACPI_PTR_DIFF(cpu_node, table);
}
pr_warn_once("PPTT table found, but unable to locate core %d (%d)\n" ,
cpu, acpi_cpu_id);
return -ENOENT;
}
static struct acpi_table_header *acpi_get_pptt(void )
{
static struct acpi_table_header *pptt;
static bool is_pptt_checked;
acpi_status status;
/*
* PPTT will be used at runtime on every CPU hotplug in path , so we
* don ' t need to call acpi_put_table ( ) to release the table mapping .
*/
if (!pptt && !is_pptt_checked) {
status = acpi_get_table(ACPI_SIG_PPTT, 0 , &pptt);
if (ACPI_FAILURE(status))
acpi_pptt_warn_missing();
is_pptt_checked = true ;
}
return pptt;
}
static int find_acpi_cpu_topology_tag(unsigned int cpu, int level, int flag)
{
struct acpi_table_header *table;
int retval;
table = acpi_get_pptt();
if (!table)
return -ENOENT;
retval = topology_get_acpi_cpu_tag(table, cpu, level, flag);
pr_debug("Topology Setup ACPI CPU %d, level %d ret = %d\n" ,
cpu, level, retval);
return retval;
}
/**
* check_acpi_cpu_flag ( ) - Determine if CPU node has a flag set
* @ cpu : Kernel logical CPU number
* @ rev : The minimum PPTT revision defining the flag
* @ flag : The flag itself
*
* Check the node representing a CPU for a given flag .
*
* Return : - ENOENT if the PPTT doesn ' t exist , the CPU cannot be found or
* the table revision isn ' t new enough .
* 1 , any passed flag set
* 0 , flag unset
*/
static int check_acpi_cpu_flag(unsigned int cpu, int rev, u32 flag)
{
struct acpi_table_header *table;
u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
struct acpi_pptt_processor *cpu_node = NULL;
int ret = -ENOENT;
table = acpi_get_pptt();
if (!table)
return -ENOENT;
if (table->revision >= rev)
cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
if (cpu_node)
ret = (cpu_node->flags & flag) != 0 ;
return ret;
}
/**
* acpi_get_cache_info ( ) - Determine the number of cache levels and
* split cache levels ( data / instruction ) and for a PE .
* @ cpu : Kernel logical CPU number
* @ levels : Number of levels if success .
* @ split_levels : Number of levels being split ( i . e . data / instruction )
* if success . Can by NULL .
*
* Given a logical CPU number , returns the number of levels of cache represented
* in the PPTT . Errors caused by lack of a PPTT table , or otherwise , return 0
* indicating we didn ' t find any cache levels .
*
* Return : - ENOENT if no PPTT table or no PPTT processor struct found .
* 0 on success .
*/
int acpi_get_cache_info(unsigned int cpu, unsigned int *levels,
unsigned int *split_levels)
{
struct acpi_pptt_processor *cpu_node;
struct acpi_table_header *table;
u32 acpi_cpu_id;
*levels = 0 ;
if (split_levels)
*split_levels = 0 ;
table = acpi_get_pptt();
if (!table)
return -ENOENT;
pr_debug("Cache Setup: find cache levels for CPU=%d\n" , cpu);
acpi_cpu_id = get_acpi_id_for_cpu(cpu);
cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
if (!cpu_node)
return -ENOENT;
acpi_count_levels(table, cpu_node, levels, split_levels);
pr_debug("Cache Setup: last_level=%d split_levels=%d\n" ,
*levels, split_levels ? *split_levels : -1 );
return 0 ;
}
/**
* cache_setup_acpi ( ) - Override CPU cache topology with data from the PPTT
* @ cpu : Kernel logical CPU number
*
* Updates the global cache info provided by cpu_get_cacheinfo ( )
* when there are valid properties in the acpi_pptt_cache nodes . A
* successful parse may not result in any updates if none of the
* cache levels have any valid flags set . Further , a unique value is
* associated with each known CPU cache entry . This unique value
* can be used to determine whether caches are shared between CPUs .
*
* Return : - ENOENT on failure to find table , or 0 on success
*/
int cache_setup_acpi(unsigned int cpu)
{
struct acpi_table_header *table;
table = acpi_get_pptt();
if (!table)
return -ENOENT;
pr_debug("Cache Setup ACPI CPU %d\n" , cpu);
cache_setup_acpi_cpu(table, cpu);
return 0 ;
}
/**
* acpi_pptt_cpu_is_thread ( ) - Determine if CPU is a thread
* @ cpu : Kernel logical CPU number
*
* Return : 1 , a thread
* 0 , not a thread
* - ENOENT , if the PPTT doesn ' t exist , the CPU cannot be found or
* the table revision isn ' t new enough .
*/
int acpi_pptt_cpu_is_thread(unsigned int cpu)
{
return check_acpi_cpu_flag(cpu, 2 , ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD);
}
/**
* find_acpi_cpu_topology ( ) - Determine a unique topology value for a given CPU
* @ cpu : Kernel logical CPU number
* @ level : The topological level for which we would like a unique ID
*
* Determine a topology unique ID for each thread / core / cluster / mc_grouping
* / socket / etc . This ID can then be used to group peers , which will have
* matching ids .
*
* The search terminates when either the requested level is found or
* we reach a root node . Levels beyond the termination point will return the
* same unique ID . The unique id for level 0 is the acpi processor id . All
* other levels beyond this use a generated value to uniquely identify
* a topological feature .
*
* Return : - ENOENT if the PPTT doesn ' t exist , or the CPU cannot be found .
* Otherwise returns a value which represents a unique topological feature .
*/
int find_acpi_cpu_topology(unsigned int cpu, int level)
{
return find_acpi_cpu_topology_tag(cpu, level, 0 );
}
/**
* find_acpi_cpu_topology_package ( ) - Determine a unique CPU package value
* @ cpu : Kernel logical CPU number
*
* Determine a topology unique package ID for the given CPU .
* This ID can then be used to group peers , which will have matching ids .
*
* The search terminates when either a level is found with the PHYSICAL_PACKAGE
* flag set or we reach a root node .
*
* Return : - ENOENT if the PPTT doesn ' t exist , or the CPU cannot be found .
* Otherwise returns a value which represents the package for this CPU .
*/
int find_acpi_cpu_topology_package(unsigned int cpu)
{
return find_acpi_cpu_topology_tag(cpu, PPTT_ABORT_PACKAGE,
ACPI_PPTT_PHYSICAL_PACKAGE);
}
/**
* find_acpi_cpu_topology_cluster ( ) - Determine a unique CPU cluster value
* @ cpu : Kernel logical CPU number
*
* Determine a topology unique cluster ID for the given CPU / thread .
* This ID can then be used to group peers , which will have matching ids .
*
* The cluster , if present is the level of topology above CPUs . In a
* multi - thread CPU , it will be the level above the CPU , not the thread .
* It may not exist in single CPU systems . In simple multi - CPU systems ,
* it may be equal to the package topology level .
*
* Return : - ENOENT if the PPTT doesn ' t exist , the CPU cannot be found
* or there is no toplogy level above the CPU . .
* Otherwise returns a value which represents the package for this CPU .
*/
int find_acpi_cpu_topology_cluster(unsigned int cpu)
{
struct acpi_table_header *table;
struct acpi_pptt_processor *cpu_node, *cluster_node;
u32 acpi_cpu_id;
int retval;
int is_thread;
table = acpi_get_pptt();
if (!table)
return -ENOENT;
acpi_cpu_id = get_acpi_id_for_cpu(cpu);
cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
if (!cpu_node || !cpu_node->parent)
return -ENOENT;
is_thread = cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD;
cluster_node = fetch_pptt_node(table, cpu_node->parent);
if (!cluster_node)
return -ENOENT;
if (is_thread) {
if (!cluster_node->parent)
return -ENOENT;
cluster_node = fetch_pptt_node(table, cluster_node->parent);
if (!cluster_node)
return -ENOENT;
}
if (cluster_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID)
retval = cluster_node->acpi_processor_id;
else
retval = ACPI_PTR_DIFF(cluster_node, table);
return retval;
}
/**
* find_acpi_cpu_topology_hetero_id ( ) - Get a core architecture tag
* @ cpu : Kernel logical CPU number
*
* Determine a unique heterogeneous tag for the given CPU . CPUs with the same
* implementation should have matching tags .
*
* The returned tag can be used to group peers with identical implementation .
*
* The search terminates when a level is found with the identical implementation
* flag set or we reach a root node .
*
* Due to limitations in the PPTT data structure , there may be rare situations
* where two cores in a heterogeneous machine may be identical , but won ' t have
* the same tag .
*
* Return : - ENOENT if the PPTT doesn ' t exist , or the CPU cannot be found .
* Otherwise returns a value which represents a group of identical cores
* similar to this CPU .
*/
int find_acpi_cpu_topology_hetero_id(unsigned int cpu)
{
return find_acpi_cpu_topology_tag(cpu, PPTT_ABORT_PACKAGE,
ACPI_PPTT_ACPI_IDENTICAL);
}
Messung V0.5 in Prozent C=97 H=96 G=96
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland