1#!/usr/bin/env python3 2# 3# Copyright (C) 2022 Intel Corporation. 4# 5# SPDX-License-Identifier: BSD-3-Clause 6# 7 8import acrn_config_utilities, board_cfg_lib 9from acrn_config_utilities import get_node 10 11# CPU frequency dependency 12# Some CPU cores may share the same clock domain/group with others, which makes them always run at 13# the same frequency of the highest on in the group. Including those known conditions: 14# 1. CPU in the clock domain described in ACPI _PSD. 15# Like _PSS, board_inspector extracted this data from Linux cpufreq driver 16# (see Linux document 'sysfs-devices-system-cpu' about freqdomain_cpus) 17# 2. CPU hyper threads sharing the same physical core. 18# The data is extracted form apic id. 19# 3. E-cores residents in the same topological group. 20# The data is extracted form CPU model type and apic id. 21# CPU frequency dependency may have some impacts on our frequency limits. 22# 23# Returns a list that contains each CPU's "dependency data". The "dependency data" is also a list 24# containing CPU_IDs that share frequency with the current one. 25# e.g. CPU 8 is sharing with CPU 9,10,11, so dependency_data[8] = ['8', '9', '10', '11'] 26def get_dependency(board_etree): 27 cpus = board_etree.xpath("//processors//thread") 28 dep_ret = [] 29 for cpu in cpus: 30 cpu_id = get_node("./cpu_id/text()", cpu) 31 psd_cpus = [cpu_id] 32 psd_cpus_list = get_node("./freqdomain_cpus/text()", cpu) 33 if psd_cpus_list != None: 34 psd_cpus = psd_cpus_list.split(' ') 35 apic_id = int(get_node("./apic_id/text()", cpu)[2:], base=16) 36 is_hybrid = (len(board_etree.xpath("//processors//capability[@id='hybrid']")) != 0) 37 core_type = get_node("./core_type/text()", cpu) 38 for other_cpu in cpus: 39 other_cpu_id = get_node("./cpu_id/text()", other_cpu) 40 if cpu_id != other_cpu_id: 41 other_apic_id = int(get_node("./apic_id/text()", other_cpu)[2:], base=16) 42 other_core_type = get_node("./core_type/text()", other_cpu) 43 # threads at same core 44 if (apic_id & ~1) == (other_apic_id & ~1): 45 psd_cpus.append(other_cpu_id) 46 # e-cores in the same group. Infered from Atom cores share the same L2 cache 47 share_cache = 0 48 if is_hybrid and core_type == 'Atom' and other_core_type == 'Atom': 49 l2cache_nodes = board_etree.xpath("//caches/cache[@level='2']") 50 for l2cache in l2cache_nodes: 51 processors = l2cache.xpath("./processors/processor/text()") 52 if '{:#x}'.format(apic_id) in processors and '{:#x}'.format(other_apic_id) in processors: 53 share_cache = 1 54 if share_cache == 1: 55 psd_cpus.append(other_cpu_id) 56 57 if psd_cpus != None: 58 psd_cpus = list(set(psd_cpus)) 59 psd_cpus.sort() 60 dep_ret.insert(int(cpu_id), psd_cpus) 61 else: 62 dep_ret.insert(int(cpu_id), None) 63 return dep_ret 64 65# CPU frequency limits: 66# 67# Frequency limits is a per CPU data type. Hypervisor uses this data to quickly decide what performance 68# level/p-state range it should apply. 69# 70# Those limits are decided by hardware and scenario config. 71# 72# When the CPU is assigned to a RTVM, we want to set its frequency fixed.(to get more certainty 73# in latency). To do this, we just let highest_lvl = lowest_lvl. 74# Some CPU cores' frequency may be linked to each other in a frequency domain or group(eg. e-cores in a group). 75# In this condition, RTVM's CPU frequency might be influenced by other VMs. So we fix all of them to the value of 76# the RTVM's CPU frequence. 77# 78# Both HWP and ACPI p-state are supported in ACRN CPU performance management. So here we generate two sets of 79# data: 80# 81# - 'limit_guaranteed_lvl', 'limit_highest_lvl' and 'limit_lowest_lvl' are for HWP. The values represent 82# HWP performance level used in IA32_HWP_CAPABILITIES and IA32_HWP_REQUEST. 83# 84# - 'limit_nominal_pstate', 'limit_highest_pstate' and 'limit_lowest_pstate' are for ACPI p-state. 85# Those values represent the performance state's index P(x). 86# ACPI p-state does not define a 'guaranteed p-state' or a 'base p-state'. Here the 'nominal p-state' refers 87# to a state whose frequency is closest to the max none-turbo frequency. 88def alloc_limits(board_etree, scenario_etree, allocation_etree): 89 cpu_has_eist = (len(board_etree.xpath("//processors//capability[@id='est']")) != 0) 90 cpu_has_hwp = (len(board_etree.xpath("//processors//capability[@id='hwp_supported']")) != 0) 91 cpu_has_turbo = (len(board_etree.xpath("//processors//capability[@id='turbo_boost_available']")) != 0) 92 rtvm_cpus = scenario_etree.xpath(f"//vm[vm_type = 'RTVM']//cpu_affinity//pcpu_id/text()") 93 cpus = board_etree.xpath("//processors//thread") 94 95 for cpu in cpus: 96 cpu_id = get_node("./cpu_id/text()", cpu) 97 if cpu_has_hwp: 98 guaranteed_performance_lvl = get_node("./guaranteed_performance_lvl/text()", cpu) 99 highest_performance_lvl = get_node("./highest_performance_lvl/text()", cpu) 100 lowest_performance_lvl = get_node("./lowest_performance_lvl/text()", cpu) 101 if cpu_id in rtvm_cpus: 102 # for CPUs in RTVM, fix to base performance 103 limit_lowest_lvl = guaranteed_performance_lvl 104 limit_highest_lvl = guaranteed_performance_lvl 105 limit_guaranteed_lvl = guaranteed_performance_lvl 106 elif cpu_has_turbo: 107 limit_lowest_lvl = lowest_performance_lvl 108 limit_highest_lvl = highest_performance_lvl 109 limit_guaranteed_lvl = guaranteed_performance_lvl 110 else: 111 limit_lowest_lvl = lowest_performance_lvl 112 limit_highest_lvl = guaranteed_performance_lvl 113 limit_guaranteed_lvl = guaranteed_performance_lvl 114 else: 115 limit_lowest_lvl = 1 116 limit_highest_lvl = 0xff 117 limit_guaranteed_lvl = 0xff 118 119 cpu_node = acrn_config_utilities.append_node(f"//hv/cpufreq/CPU", None, allocation_etree, id = cpu_id) 120 limit_node = acrn_config_utilities.append_node("./limits", None, cpu_node) 121 acrn_config_utilities.append_node("./limit_guaranteed_lvl", limit_guaranteed_lvl, limit_node) 122 acrn_config_utilities.append_node("./limit_highest_lvl", limit_highest_lvl, limit_node) 123 acrn_config_utilities.append_node("./limit_lowest_lvl", limit_lowest_lvl, limit_node) 124 125 limit_highest_pstate = 0 126 limit_nominal_pstate = 0 127 limit_lowest_pstate = 0 128 if cpu_has_eist: 129 mntr = board_etree.xpath("//processors//attribute[@id='max_none_turbo_ratio']/text()") 130 none_turbo_p = 0 131 p_count = board_cfg_lib.get_p_state_count() 132 if len(mntr) != 0: 133 none_turbo_p = board_cfg_lib.get_p_state_index_from_ratio(int(mntr[0])) 134 if p_count != 0: 135 # P0 is the highest stat 136 if cpu_id in rtvm_cpus: 137 # for CPUs in RTVM, fix to nominal performance(max none turbo frequency if turbo on) 138 if cpu_has_turbo: 139 limit_highest_pstate = none_turbo_p 140 limit_nominal_pstate = none_turbo_p 141 limit_lowest_pstate = none_turbo_p 142 else: 143 limit_highest_pstate = 0 144 limit_nominal_pstate = 0 145 limit_lowest_pstate = 0 146 else: 147 if cpu_has_turbo: 148 limit_highest_pstate = 0 149 limit_nominal_pstate = none_turbo_p 150 limit_lowest_pstate = p_count -1 151 else: 152 limit_highest_pstate = 0 153 limit_nominal_pstate = 0 154 limit_lowest_pstate = p_count -1 155 156 acrn_config_utilities.append_node("./limit_nominal_pstate", str(limit_nominal_pstate), limit_node) 157 acrn_config_utilities.append_node("./limit_highest_pstate", str(limit_highest_pstate), limit_node) 158 acrn_config_utilities.append_node("./limit_lowest_pstate", str(limit_lowest_pstate), limit_node) 159 160 # Let CPUs in the same frequency dependency group have the same limits. So that RTVM's frequency can be fixed 161 dep_info = get_dependency(board_etree) 162 for alloc_cpu in allocation_etree.xpath("//cpufreq/CPU"): 163 dependency_cpus = dep_info[int(alloc_cpu.attrib['id'])] 164 if get_node("./limits", alloc_cpu) != None: 165 highest_lvl = int(get_node(".//limit_highest_lvl/text()", alloc_cpu)) 166 lowest_lvl = int(get_node(".//limit_lowest_lvl/text()", alloc_cpu)) 167 highest_pstate = int(get_node(".//limit_highest_pstate/text()", alloc_cpu)) 168 lowest_pstate = int(get_node(".//limit_lowest_pstate/text()", alloc_cpu)) 169 170 for dep_cpu_id in dependency_cpus: 171 dep_highest_lvl = int(get_node(f"//cpufreq/CPU[@id={dep_cpu_id}]//limit_highest_lvl/text()", allocation_etree)) 172 dep_lowest_lvl = int(get_node(f"//cpufreq/CPU[@id={dep_cpu_id}]//limit_lowest_lvl/text()", allocation_etree)) 173 if highest_lvl > dep_highest_lvl: 174 highest_lvl = dep_highest_lvl 175 if lowest_lvl < dep_lowest_lvl: 176 lowest_lvl = dep_lowest_lvl 177 dep_highest_pstate = int(get_node(f"//cpufreq/CPU[@id={dep_cpu_id}]//limit_highest_pstate/text()", allocation_etree)) 178 dep_lowest_pstate = int(get_node(f"//cpufreq/CPU[@id={dep_cpu_id}]//limit_lowest_pstate/text()", allocation_etree)) 179 if highest_pstate < dep_highest_pstate: 180 highest_pstate = dep_highest_pstate 181 if lowest_pstate > dep_lowest_pstate: 182 lowest_pstate = dep_lowest_pstate 183 184 acrn_config_utilities.update_text("./limits/limit_highest_lvl", str(highest_lvl), alloc_cpu, True) 185 acrn_config_utilities.update_text("./limits/limit_lowest_lvl", str(lowest_lvl), alloc_cpu, True) 186 acrn_config_utilities.update_text("./limits/limit_highest_pstate", str(highest_pstate), alloc_cpu, True) 187 acrn_config_utilities.update_text("./limits/limit_lowest_pstate", str(lowest_pstate), alloc_cpu, True) 188 189def fn(board_etree, scenario_etree, allocation_etree): 190 acrn_config_utilities.append_node("/acrn-config/hv/cpufreq", None, allocation_etree) 191 alloc_limits(board_etree, scenario_etree, allocation_etree) 192