Received: by 2002:a05:6a10:f3d0:0:0:0:0 with SMTP id a16csp4661021pxv; Tue, 6 Jul 2021 06:23:35 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyDkCVGYvv2VP81OEmsHAYZnff9AKrC3Cry101B4MkYR//4r2o6x5+Is2tEY4DjLEo/O2vo X-Received: by 2002:a17:907:1c98:: with SMTP id nb24mr19028163ejc.316.1625577815082; Tue, 06 Jul 2021 06:23:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1625577815; cv=none; d=google.com; s=arc-20160816; b=UoNHR7mZh5UL51PnwER1A46eDpuH374csfdp/VCp/sqQNYWU/wKnhr8L3takMM4svu CFvQqFSvCTdJ/O3cBp50bADW9GGS9ZrkplbQPyVDcU18gq+lJ3nn4KvAl3ITt4lKyfv3 0qWUNB2sUi/ITVsUCic3c9mVFrIQebNNIMean3Gwz4VC8LDL51TBrmr6mHVN6EfSNY0n 4HQCNHsUWIEK69gP4ecdACghDwsp+MYB7s0WrYWnry8ZF9wty1C/eZf7WKVJvY1OvBPf xu/a2cKtVRB+45Q5eQ2sIk63sROeUJlym+O4cZmh4zlUdWALc4MbVJk05OaDH4MtqLGP mtfQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:references:in-reply-to:message-id:date:subject :cc:to:from; bh=FAkxC7Vwvy+gY/AvgOdWt3cV436gsVCv+lY+6HfdZQM=; b=DOSa9kNN6BNar2xxyocnRrw97VVKVnW6gdnexAxdk78oW/1fQ1q1f3Qx58XUvkUabH iv8A/sXbPQYW9Od+OwPhPW8vZ3BCnecUrugbbRQrRyBg5M0VgpC9ys3cy+vRQsXgkK7x J+z3FCtjmqK8WM6A6wUXL1BFjNDtD1lEtCopiOQefR7LpTwzwdP7Gr6EMkSluR8vDs2a 0T1GGE63cjd9ARWO2HYXqGts+bQmh9qIYC6HYwzH5vLt8xEpdN8SWAoGABGhHzVkRbhA CFDYm05OrAPhTLtletulFkfH1v9iTGzOE5ICUK9jkpuFVXFG46VNT7cDdyECaK7Iw3/B Xdqg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=arm.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id g17si9955473edp.82.2021.07.06.06.23.11; Tue, 06 Jul 2021 06:23:35 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=arm.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231969AbhGFNVh (ORCPT + 99 others); Tue, 6 Jul 2021 09:21:37 -0400 Received: from foss.arm.com ([217.140.110.172]:42200 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231935AbhGFNVd (ORCPT ); Tue, 6 Jul 2021 09:21:33 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 81F131FB; Tue, 6 Jul 2021 06:18:54 -0700 (PDT) Received: from e123648.arm.com (unknown [10.57.7.228]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 584153F73B; Tue, 6 Jul 2021 06:18:52 -0700 (PDT) From: Lukasz Luba To: linux-kernel@vger.kernel.org, daniel.lezcano@linaro.org Cc: linux-pm@vger.kernel.org, amitk@kernel.org, rui.zhang@intel.com, lukasz.luba@arm.com, dietmar.eggemann@arm.com, Chris.Redpath@arm.com, Beata.Michalska@arm.com, viresh.kumar@linaro.org, rjw@rjwysocki.net, amit.kachhap@gmail.com Subject: [RFC PATCH v2 6/6] thermal: cpufreq_cooling: Improve power estimation based on Active Stats framework Date: Tue, 6 Jul 2021 14:18:28 +0100 Message-Id: <20210706131828.22309-7-lukasz.luba@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210706131828.22309-1-lukasz.luba@arm.com> References: <20210706131828.22309-1-lukasz.luba@arm.com> Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The cpufreq_cooling has dedicated APIs for thermal governor called Intelligent Power Allocation (IPA). IPA needs the CPUs power used by the devices in the past. Based on this, IPA tries to estimate the power budget, allocate new budget and split it across cooling devices for the next period (keeping the system in the thermal envelope). When the input power estimated value has big error, the whole mechanism does not work properly. The current power estimation assumes constant frequency during the whole IPA period (e.g. 100ms). This can cause big error in the power estimation, especially when SchedUtil governor is used and frequency is often adjusted to the current need. This can be visible in periodic workloads, when the frequency oscillates between two OPPs and IPA samples the lower frequency. This patch introduces a new mechanism which solves this frequency sampling problem. It uses Active Stats framework to track and account the CPU power used for a given IPA period. Signed-off-by: Lukasz Luba --- drivers/thermal/cpufreq_cooling.c | 132 ++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/drivers/thermal/cpufreq_cooling.c b/drivers/thermal/cpufreq_cooling.c index eeb4e4b76c0b..90dca20e458a 100644 --- a/drivers/thermal/cpufreq_cooling.c +++ b/drivers/thermal/cpufreq_cooling.c @@ -10,6 +10,7 @@ * Viresh Kumar * */ +#include #include #include #include @@ -61,6 +62,7 @@ struct time_in_idle { * @policy: cpufreq policy. * @idle_time: idle time stats * @qos_req: PM QoS contraint to apply + * @ast_mon: Active Stats Monitor array of pointers * * This structure is required for keeping information of each registered * cpufreq_cooling_device. @@ -75,6 +77,9 @@ struct cpufreq_cooling_device { struct time_in_idle *idle_time; #endif struct freq_qos_request qos_req; +#ifdef CONFIG_ACTIVE_STATS + struct active_stats_monitor **ast_mon; +#endif }; #ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR @@ -124,6 +129,107 @@ static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev, return cpufreq_cdev->em->table[i].frequency; } +#ifdef CONFIG_ACTIVE_STATS +static u32 account_cpu_power(struct active_stats_monitor *ast_mon, + struct em_perf_domain *em) +{ + u64 single_power, residency, total_time; + struct active_stats_state *result; + u32 power = 0; + int i; + + mutex_lock(&ast_mon->lock); + result = ast_mon->snapshot.result; + total_time = ast_mon->local_period; + + for (i = 0; i < ast_mon->states_count; i++) { + residency = result->residency[i]; + single_power = em->table[i].power * residency; + single_power = div64_u64(single_power, total_time); + power += (u32)single_power; + } + + mutex_unlock(&ast_mon->lock); + + return power; +} + +static u32 get_power_est(struct cpufreq_cooling_device *cdev) +{ + int num_cpus, ret, i; + u32 total_power = 0; + + num_cpus = cpumask_weight(cdev->policy->related_cpus); + + for (i = 0; i < num_cpus; i++) { + ret = active_stats_cpu_update_monitor(cdev->ast_mon[i]); + if (ret) + return 0; + + total_power += account_cpu_power(cdev->ast_mon[i], cdev->em); + } + + return total_power; +} + +static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, + u32 *power) +{ + struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; + struct cpufreq_policy *policy = cpufreq_cdev->policy; + + *power = get_power_est(cpufreq_cdev); + + trace_thermal_power_cpu_get_power(policy->related_cpus, 0, 0, 0, + *power); + + return 0; +} + +static void clean_cpu_monitoring(struct cpufreq_cooling_device *cdev) +{ + int num_cpus, i; + + if (!cdev->ast_mon) + return; + + num_cpus = cpumask_weight(cdev->policy->related_cpus); + + for (i = 0; i < num_cpus; i++) + active_stats_cpu_free_monitor(cdev->ast_mon[i++]); + + kfree(cdev->ast_mon); + cdev->ast_mon = NULL; +} + +static int setup_cpu_monitoring(struct cpufreq_cooling_device *cdev) +{ + int cpu, cpus, i = 0; + + if (cdev->ast_mon) + return 0; + + cpus = cpumask_weight(cdev->policy->related_cpus); + + cdev->ast_mon = kcalloc(cpus, sizeof(struct active_stats_monitor *), + GFP_KERNEL); + if (!cdev->ast_mon) + return -ENOMEM; + + for_each_cpu(cpu, cdev->policy->related_cpus) { + cdev->ast_mon[i] = active_stats_cpu_setup_monitor(cpu); + if (IS_ERR_OR_NULL(cdev->ast_mon[i++])) + goto cleanup; + } + + return 0; + +cleanup: + clean_cpu_monitoring(cdev); + return -EINVAL; +} +#else + /** * get_load() - get load for a cpu * @cpufreq_cdev: struct cpufreq_cooling_device for the cpu @@ -184,6 +290,15 @@ static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_cdev, return (raw_cpu_power * cpufreq_cdev->last_load) / 100; } +static void clean_cpu_monitoring(struct cpufreq_cooling_device *cpufreq_cdev) +{ +} + +static int setup_cpu_monitoring(struct cpufreq_cooling_device *cpufreq_cdev) +{ + return 0; +} + /** * cpufreq_get_requested_power() - get the current power * @cdev: &thermal_cooling_device pointer @@ -252,6 +367,7 @@ static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, return 0; } +#endif /** * cpufreq_state2power() - convert a cpu cdev state to power consumed @@ -323,6 +439,20 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev, return 0; } +static int cpufreq_change_governor(struct thermal_cooling_device *cdev, + bool governor_up) +{ + struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; + int ret = 0; + + if (governor_up) + ret = setup_cpu_monitoring(cpufreq_cdev); + else + clean_cpu_monitoring(cpufreq_cdev); + + return ret; +} + static inline bool em_is_sane(struct cpufreq_cooling_device *cpufreq_cdev, struct em_perf_domain *em) { struct cpufreq_policy *policy; @@ -566,6 +696,7 @@ __cpufreq_cooling_register(struct device_node *np, cooling_ops->get_requested_power = cpufreq_get_requested_power; cooling_ops->state2power = cpufreq_state2power; cooling_ops->power2state = cpufreq_power2state; + cooling_ops->change_governor = cpufreq_change_governor; } else #endif if (policy->freq_table_sorted == CPUFREQ_TABLE_UNSORTED) { @@ -690,6 +821,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) thermal_cooling_device_unregister(cdev); freq_qos_remove_request(&cpufreq_cdev->qos_req); + clean_cpu_monitoring(cpufreq_cdev); free_idle_time(cpufreq_cdev); kfree(cpufreq_cdev); } -- 2.17.1