From patchwork Tue Oct 6 12:20:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Lezcano X-Patchwork-Id: 314088 Delivered-To: patch@linaro.org Received: by 2002:a92:d603:0:0:0:0:0 with SMTP id w3csp517446ilm; Tue, 6 Oct 2020 05:21:01 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyqbaNpcg9icdzsZwKxNZuQH7zSBoCCFegLqkhd8+OOcqkWClFsernm9SAsIAhquKKudixF X-Received: by 2002:a17:906:ad8a:: with SMTP id la10mr2919359ejb.176.1601986861629; Tue, 06 Oct 2020 05:21:01 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1601986861; cv=none; d=google.com; s=arc-20160816; b=FULm9Z6gRP4ze3zJSOpNYl2EEpZthhw7crvN551Py+YLw2Af6rpgQspdZ+z46yyZWP U5bdiaTWbgOZc+jZRuvqToL2wPoIDsW5NufMVdvkSj+jY023m1reI16emO6fr/POpQhI 2yngdpDXRs8JgYhmSEQ1JGKbCRxTJvURBBLsNFFXCw5VLlMxZf6oDFCKjePr+OoMd7US c34v/1FhmmAxI7MgqvYAPZkPj+2JTWsQW0FzF6TJvCv05GiJNjZ5LrOhVsREJqr5V7nx zVV8jnT3LTwq2Ws9m1+VESsEi0k8ZTGiSNnM/2n7F+zyCxb8JCZC3xScdoRI6niaUddo Lhxg== 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:dkim-signature; bh=xF/1RSp7bqmtycW1zGKYYAA7v8YwsJOV1VWvLd51HvY=; b=QVT+oErwpaeq/5Ut/SyuONqNlbnvC8ATpSN/8MSi9IZ2oEIl0KZvYiRgSeU+iMVxrb 5dSUbZXODya07yUKD8fcY/XCI6kybgVOoZ0TbziQwqgCBMW2FUJAOjFrjdYdd+nSy5fQ nrcKB5+gPD3MVtgi1XZNIytGvJJbNpCKo9dG1BbxIqXkeG/ZuvipFQUg6Pr+uWjVdtPB RU4kGm+rt4BhvyzVxUKcrUsD85IgidmuOOXLB3Hk9PuwiuNhRSqoZm43f0CHQ4I+UFBZ zz/XQebNrv/Aou2JVNK7R++R97juEOPGiaWr42Go33C5P67PRtpj42IgHtCS7ZgiiXIH YbNg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=JNyxWDqW; spf=pass (google.com: domain of linux-pm-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-pm-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id gw9si1895004ejb.176.2020.10.06.05.21.01; Tue, 06 Oct 2020 05:21:01 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-pm-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=JNyxWDqW; spf=pass (google.com: domain of linux-pm-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-pm-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726404AbgJFMUv (ORCPT + 9 others); Tue, 6 Oct 2020 08:20:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44108 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726391AbgJFMUv (ORCPT ); Tue, 6 Oct 2020 08:20:51 -0400 Received: from mail-wm1-x343.google.com (mail-wm1-x343.google.com [IPv6:2a00:1450:4864:20::343]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D2384C0613D1 for ; Tue, 6 Oct 2020 05:20:50 -0700 (PDT) Received: by mail-wm1-x343.google.com with SMTP id l11so2707862wmh.2 for ; Tue, 06 Oct 2020 05:20:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=xF/1RSp7bqmtycW1zGKYYAA7v8YwsJOV1VWvLd51HvY=; b=JNyxWDqWBJo/CJXTGKT3YpwfrRWtPB4ugqyRr1olCTfp3iwejoyRXJ+MNVy5D+F6t7 fdTkqXxeWNNoa97xO2nzoK/DwcjRHBtG3ICdkcaBK47O2ri8wGfGGCNdtoZD9SARwm5D LTrKwFRg0j23t6TkXX/7XfecgS9QeYbUtC22nKTabsE5b9LrairpLN1lqhWjvJ0Dc5rM WzS+mafPqZJV/d/5Rkt+BYrtTFrIrCSDdvyu3oRJ4WNPWq9botdJXVmhqz1c/09yZZj2 gnegKviwWZ0ygOWa9lM4sOGjWVldp83ar7kFclo2/HEUX7ZAAq/3VT+xYXBv+UROX8NZ nR7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=xF/1RSp7bqmtycW1zGKYYAA7v8YwsJOV1VWvLd51HvY=; b=JoAOtmA73Dqw+VbZMjOmJaPgrvwSK/CKoGkKv5RVPB/ZB/w12dzFWdgilObQBH9dV0 n12Jj5bRrprXc+W1Ig5Nv5hIG5dOLXwOKQmK3EF9RT+i9jgj/EhX3eCsTcUAZeq3oRpt /usavVeUmgfDsBZZzISLgNH57fpfU5s5k1Lle2hMVldaKputOYovlb1l0QCdT18UDEWN 6+tLwxcIBfOclbFXsYKZ+nbAc7Iv0Jk+TLyPWtUzJS/u+q8K2AiG5fGRbR+TVGuGM3G4 BKvge6clQwAfRkNGYuoqqDuS1pupEqiMPEfqM2u8HdYV+VqNfLZFxk7zWawlgU7cYe03 Dm8A== X-Gm-Message-State: AOAM5301r6DekO/yJOOGcb9wsuti/XoJ4CIdYeOTocBy+0FR2TkD9kf6 fDv5hwiGjwdzEb18bKVz5Dm2FQ== X-Received: by 2002:a1c:66d5:: with SMTP id a204mr4412406wmc.62.1601986849332; Tue, 06 Oct 2020 05:20:49 -0700 (PDT) Received: from localhost.localdomain (lns-bzn-59-82-252-130-8.adsl.proxad.net. [82.252.130.8]) by smtp.gmail.com with ESMTPSA id s19sm3742521wmc.41.2020.10.06.05.20.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Oct 2020 05:20:48 -0700 (PDT) From: Daniel Lezcano To: rafael@kernel.org, srinivas.pandruvada@linux.intel.com Cc: lukasz.luba@arm.com, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, rui.zhang@intel.com, Andrew Morton , Andy Shevchenko , Akinobu Mita Subject: [PATCH 1/4] units: Add Watt units Date: Tue, 6 Oct 2020 14:20:21 +0200 Message-Id: <20201006122024.14539-2-daniel.lezcano@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201006122024.14539-1-daniel.lezcano@linaro.org> References: <20201006122024.14539-1-daniel.lezcano@linaro.org> Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org As there are the temperature units, let's add the Watt macros definition. Signed-off-by: Daniel Lezcano --- include/linux/units.h | 4 ++++ 1 file changed, 4 insertions(+) -- 2.17.1 Reviewed-by: Lukasz Luba diff --git a/include/linux/units.h b/include/linux/units.h index aaf716364ec3..92c234e71cab 100644 --- a/include/linux/units.h +++ b/include/linux/units.h @@ -4,6 +4,10 @@ #include +#define MILLIWATT_PER_WATT 1000L +#define MICROWATT_PER_MILLIWATT 1000L +#define MICROWATT_PER_WATT 1000000L + #define ABSOLUTE_ZERO_MILLICELSIUS -273150 static inline long milli_kelvin_to_millicelsius(long t) From patchwork Tue Oct 6 12:20:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Lezcano X-Patchwork-Id: 286841 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 911F4C41604 for ; Tue, 6 Oct 2020 12:21:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 298EF20FC3 for ; Tue, 6 Oct 2020 12:21:02 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="d0uuVUai" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726499AbgJFMVA (ORCPT ); Tue, 6 Oct 2020 08:21:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44130 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726182AbgJFMU7 (ORCPT ); Tue, 6 Oct 2020 08:20:59 -0400 Received: from mail-wm1-x344.google.com (mail-wm1-x344.google.com [IPv6:2a00:1450:4864:20::344]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 05A35C061755 for ; Tue, 6 Oct 2020 05:20:58 -0700 (PDT) Received: by mail-wm1-x344.google.com with SMTP id 13so2725286wmf.0 for ; Tue, 06 Oct 2020 05:20:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=MYjkJSPSiLs6Kw7doNztBkznwqoyjPfBbc2ahKMdbtE=; b=d0uuVUaiKVZo/uJHIyV8T3AfP/3Pjv8oIT6c+JBXSxUMFZIWIPJG9jNjIoEwclkD1b ZzqG8di/PI3Om7OljkoAhLofROh0YIcdtC5ID7iqcNfeXvgNjldYNUA0Nl4quykHf9bU TZmw4ai/IFmhfoJYwB8/xjN9H63oEOlglJegBnGhKIhimqSyNNaMbZwbgzJwrPTnB0yH AdipZ3UrdvnYwJhoDhs/Cq7I7sSnibs02LngNyxbi7Pcd8kz5HKH1NdRz7n8eaJd5gvu YcaJKqBWmQyt2kzVmYjk33/ppeejixCH4R490fJ5wWYuR6mscfOcEFH2o31Dhdm5diQl GXWA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=MYjkJSPSiLs6Kw7doNztBkznwqoyjPfBbc2ahKMdbtE=; b=mx4zBkYvKlaRDSkpG5zv1Z4WyRvje5RXjxOd/hATEKTqkJDuP7e8ywpk5L148ZEUjk 2SihUPZ8Sxo3VPbTbMIufo21SJSyQfQPhF0Hq1kckrRthnaRHja6pqWqWBdtT6vnCGg8 ZueEM5VWYG8hu/r9Lgodd1/HrcIcS4Dpp02G1+v3OwaDSdADAxvSum1cst4Q6380ZoQL yHfA6e/ZOTauNVjAMmKHcRQTfMa98Me2pAY4ffkVnZipCOgqkPcHsFXq/wdG0RVapMBX lcDK386lYmea5ATka3BRPPmEQQY+oJyU462gJ8PFBO+G+jjQgTR4WhOefOfAy3+npR+s RywA== X-Gm-Message-State: AOAM531x6lDUbjHVL9PEolUuvDRewz5j3MhEPv25IKPJCH80iEdhvr8f 3lAUBJS9iZGVAzLmXCwknSAjww== X-Google-Smtp-Source: ABdhPJyXhl3+UEYrgeX/RLGRc1kcYm8V80cm0o4VixNQkHinRoYlDMr7n4MLfunVPq0tqCFcYxcpjQ== X-Received: by 2002:a1c:9dcf:: with SMTP id g198mr4461277wme.42.1601986856459; Tue, 06 Oct 2020 05:20:56 -0700 (PDT) Received: from localhost.localdomain (lns-bzn-59-82-252-130-8.adsl.proxad.net. [82.252.130.8]) by smtp.gmail.com with ESMTPSA id s19sm3742521wmc.41.2020.10.06.05.20.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Oct 2020 05:20:56 -0700 (PDT) From: Daniel Lezcano To: rafael@kernel.org, srinivas.pandruvada@linux.intel.com Cc: lukasz.luba@arm.com, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, rui.zhang@intel.com, "Rafael J. Wysocki" , Len Brown , Pavel Machek Subject: [PATCH 2/4] Documentation/powercap/dtpm: Add documentation for dtpm Date: Tue, 6 Oct 2020 14:20:22 +0200 Message-Id: <20201006122024.14539-3-daniel.lezcano@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201006122024.14539-1-daniel.lezcano@linaro.org> References: <20201006122024.14539-1-daniel.lezcano@linaro.org> Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org The dynamic thermal and power management is a technique to dynamically adjust the power consumption of different devices in order to ensure a global thermal constraint. An userspace daemon is usually monitoring the temperature and the power to take immediate action on the device. The DTPM framework provides an unified API to userspace to act on the power. Document this framework. Signed-off-by: Daniel Lezcano --- Documentation/power/powercap/dtpm.rst | 222 ++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 Documentation/power/powercap/dtpm.rst diff --git a/Documentation/power/powercap/dtpm.rst b/Documentation/power/powercap/dtpm.rst new file mode 100644 index 000000000000..ce11cf183994 --- /dev/null +++ b/Documentation/power/powercap/dtpm.rst @@ -0,0 +1,222 @@ +========================================== +Dynamic Thermal Power Management framework +========================================== + +On the embedded world, the complexity of the SoC leads to an +increasing number of hotspots which need to be monitored and mitigated +as a whole in order to prevent the temperature to go above the +normative and legally stated 'skin temperature'. + +Another aspect is to sustain the performance for a given power budget, +for example virtual reality where the user can feel dizziness if the +performance is capped while a big CPU is processing something else. Or +reduce the battery charging because the dissipated power is too high +compared with the power consumed by other devices. + +The userspace is the most adequate place to dynamically act on the +different devices by limiting their power given an application +profile: it has the knowledge of the platform. + +The Dynamic Thermal Power Management (DTPM) is a technique acting on +the device power by limiting and/or balancing a power budget among +different devices. + +The DTPM framework provides an unified interface to act on the +device power. + +=========== +1. Overview +=========== + +The DTPM framework relies on the powercap framework to create the +powercap entries in the sysfs directory and implement the backend +driver to do the connection with the power manageable device. + +The DTPM is a tree representation describing the power constraints +shared between devices, not their physical positions. + +The nodes of the tree are a virtual description aggregating the power +characteristics of the children nodes and their power limitations. + +The leaves of the tree are the real power manageable devices. + +For instance: + + SoC + | + `-- pkg + | + |-- pd0 (cpu0-3) + | + `-- pd1 (cpu4-5) + +* The pkg power will be the sum of pd0 and pd1 power numbers. + + SoC (400mW - 3100mW) + | + `-- pkg (400mW - 3100mW) + | + |-- pd0 (100mW - 700mW) + | + `-- pd1 (300mW - 2400mW) + +* When the nodes are inserted in the tree, their power characteristics + are propagated to the parents. + + SoC (600mW - 5900mW) + | + |-- pkg (400mW - 3100mW) + | | + | |-- pd0 (100mW - 700mW) + | | + | `-- pd1 (300mW - 2400mW) + | + `-- pd2 (200mW - 2800mW) + +* Each node have a weight on a 2^10 basis reflecting the percentage of + power consumption along the siblings. + + SoC (w=1024) + | + |-- pkg (w=538) + | | + | |-- pd0 (w=231) + | | + | `-- pd1 (w=794) + | + `-- pd2 (w=486) + + Note the sum of weights at the same level are equal to 1024. + +* When a power limitation is applied to a node, then it is distributed + along the children given their weights. For example, if we set a + power limitation of 3200mW at the 'SoC' root node, the resulting + tree will be. + + SoC (w=1024) <--- power_limit = 3200mW + | + |-- pkg (w=538) --> power_limit = 1681mW + | | + | |-- pd0 (w=231) --> power_limit = 378mW + | | + | `-- pd1 (w=794) --> power_limit = 1303mW + | + `-- pd2 (w=486) --> power_limit = 1519mW + +==================== +1.1 Flat description +==================== + +A root node is created and it is the parent of all the nodes. This +description is the simplest one and it is supposed to give to +userspace a flat representation of all the devices supporting the +power limitation without any power limitation distribution. + +============================ +1.2 Hierarchical description +============================ + +The different devices supporting the power limitation are represented +hierarchically. There is one root node, all intermediate nodes are +grouping the child nodes which can be intermediate nodes also or real +devices. + +The intermediate nodes aggregate the power information and allows to +set the power limit given the weight of the nodes. + +================ +2. Userspace API +================ + +As stated in the overview, the DTPM framework is built on top of the +powercap framework. Thus the sysfs interface is the same, please refer +to the powercap documentation for further details. + + * power_uw: Instantaneous power consumption. If the node is an + intermediate node, then the power consumption will be the sum of all + children power consumption. + + * max_power_range_uw: The power range resulting of the maximum power + minus the minimum power. + + * name: The name of the node. This is implementation dependant. Even + if it is not recommended for the userspace, several nodes can have + the same name. + + * constraint_X_name: The name of the constraint. + + * constraint_X_max_power_uw: The maximum power limit to be applicable + to the node. + + * constraint_X_power_limit_uw: The power limit to be applied to the + node. If the value contained in constraint_X_max_power_uw is set, + the constraint will be removed. + + * constraint_X_time_window_us: The meaning of this file will depend + on the constraint number. + +=============== +2.1 Constraints +=============== + + * Constraint 0: The power limitation is immediately applied, without + limitation in time. + +============= +3. Kernel API +============= + +============ +3.1 Overview +============ + +The DTPM framework has no power limiting backend support. It is +generic and provides a set of API to let the different drivers to +implement the backend part for the power limitation and create a the +power constraints tree. + +It is up to the platform to provide the initialization function to +allocate and link the different nodes of the tree. + +A special macro has the role of declaring a node and the corresponding +initialization function via a description structure. This one contains +an optional parent field allowing to hook different devices to an +already existing tree at boot time. + +struct dtpm_descr my_descr = { + .name = "my_name", + .init = my_init_func, +}; + +DTPM_DECLARE(my_descr); + +The nodes of the DTPM tree are described with dtpm structure. The +steps to add a new power limitable device is done in three steps: + + * Allocate the dtpm node + * Set the power number of the dtpm node + * Register the dtpm node + +The registration of the dtpm node is done with the powercap +ops. Basically, it must implements the callbacks to get and set the +power and the limit. + +Alternatively, if the node to be inserted is an intermediate one, then +a simple function to insert it as a future parent is available. + +If a device has its power characteristics changing, then the tree must +be updated with the new power numbers and weights. + +================ +3.2 Nomenclature +================ + + * dtpm_alloc() : Allocate and initialize a dtpm structure + + * dtpm_register() : Add the dtpm node to the tree + + * dtpm_register_parent() : Add an intermediate node + + * dtpm_unregister() : Remove the dtpm node from the tree + + * dtpm_update_power() : Update the power characteristics of the dtpm node From patchwork Tue Oct 6 12:20:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Lezcano X-Patchwork-Id: 314090 Delivered-To: patch@linaro.org Received: by 2002:a92:d603:0:0:0:0:0 with SMTP id w3csp517581ilm; Tue, 6 Oct 2020 05:21:11 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxczgNdqKTgOrkxKNqkwJ2ijZ9sqvIuiMOBuxZPwso3enbvscVQ0GCMxvclVxw54OADGhoP X-Received: by 2002:aa7:cc02:: with SMTP id q2mr4950163edt.196.1601986871041; Tue, 06 Oct 2020 05:21:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1601986871; cv=none; d=google.com; s=arc-20160816; b=XkBoCi7NuHjVl6pMrXdQrof8DLfvjJGKjWmebqfbl1CDR5QiIvB5UM3xHnRXzLPxsp 8AvDe/Q+xqbg+eARvAn55Y2kJrOC+pVlli2KeMIwjqRRNHD/4a4wejhzJgHGaYAjRgSI bWoljidiNB6HW7aHp3f2sbMV7Zozi4Q4mvWPyC75l6PgG8b4IVlsgZGYdBIWkzdBqQH2 6StWWosVolIkRnB61nYYQja6Kow6qXh5hLEj4qei/vlo4kfru+Xcv0UxIiHDSfB14kgb YQuRDZ0bf+lC7HmE1xl7Ba3gfcnmdNBmAO5Rz+RfAsVg6kcs/UgbHafySIdCerDZeUhr 10pA== 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:dkim-signature; bh=3LkwJf7/8QhmcT3hOLsXffvH/dghnsDpAfit/51wzXQ=; b=vvg4qbHMHVmZGRiIfkMbITleDidKSoqVL0Nl/rq1U9xEvZxp7HY991DUeSa6NXQ0Ij Dd6CPhfpXCGnupVoqmqH/eZGP+l5/hVEYaOoFpOFWkvO9SLtqpDsx+yxcgTmRqL5b0Ux LjI+ireEhKpgLvSiLm3nJY24QJB7FGk8CAPeOsYD56AwojwgcpDqV4YwKfFsvsQO4nxT ZCegPFpsMOfonzapFDTU+XA2viABFuikt8OCH9qdyWgEVCn+ntMmgif3FUxjw9dP7nZw /rDEMQUiz0h3dcbM2p3EGPq2Fd7BcG7CoS0ghaSYYVsPmsnS3v5xKTzq0SWbwNVeJI+K nbyA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b="BSBu/hXk"; spf=pass (google.com: domain of linux-pm-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-pm-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id w12si2555728edi.441.2020.10.06.05.21.10; Tue, 06 Oct 2020 05:21:11 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-pm-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b="BSBu/hXk"; spf=pass (google.com: domain of linux-pm-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-pm-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726519AbgJFMVF (ORCPT + 9 others); Tue, 6 Oct 2020 08:21:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44154 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726504AbgJFMVF (ORCPT ); Tue, 6 Oct 2020 08:21:05 -0400 Received: from mail-wm1-x342.google.com (mail-wm1-x342.google.com [IPv6:2a00:1450:4864:20::342]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D3687C0613D1 for ; Tue, 6 Oct 2020 05:21:04 -0700 (PDT) Received: by mail-wm1-x342.google.com with SMTP id p15so2695328wmi.4 for ; Tue, 06 Oct 2020 05:21:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=3LkwJf7/8QhmcT3hOLsXffvH/dghnsDpAfit/51wzXQ=; b=BSBu/hXkCC3YXHMWu6O2zl2b1dp50s6lb9wl5j8cAbnVuUEByWMBrXxlwI1/+arpCI ob0bDGSZkNY/v1ES93ohvjRopIen6Ihp6RwFiDwRGkm5MQX5gFPOEM6+Lbf/l70OcyGC b+Bd5P487zrEUEgaOMRqq4g7eaia9B0uFREyiltNZCKSMn/Rc6Pb0h9u/cZpczyeYrpd asPV4kL/1McvD5cbXtfSEtsDbxTMAVu+WHQYNwUpny9g9+yaNjUtBPQVbTHMidi08qZn GIliuDNWWYEAWg4LWHhijLCLDlLU9Qa0D0Rm0v5V8Kwz0ePZR/+wlYVveIjKqsL7Z87y v1ZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=3LkwJf7/8QhmcT3hOLsXffvH/dghnsDpAfit/51wzXQ=; b=jBdStUaFf4mGdjO2b8msFWhjJqdhU3V2R0hpu/zXJjBvyXnqNO1op9dVRf1LI1YxwU Pf0yVc84vbNzVIIiGrO/PB2874fp15GH/qABAR5jRkAQ/3CEMkOlG3DHF5I97gmeEjMg MHRKU8HlR30F5x4XJkeZSucSnLl2dLX+34zGSZJfyypa88cLp7vDGhTH2R/cUI8kLJVl mdfXW455ZXKDuuJc7fS6t3eHCOEgGnwBpblARQH8Rk0Hof2xgGebw5E7ZVZ/DQ00FB2w YSfmF4oKRn6tMhYTP7MUoPURjNgcgY+6GKOB2DgvwWwkIG6aTQlg03VJNqe7Q05AaGyI V/5g== X-Gm-Message-State: AOAM5336eDfHbW4gOinNGjkNZDHYd1dEgDLOeNmum6vyijqmyMeB/Mug uUZ50Wp4j9mQqBM0QradFRpGaw== X-Received: by 2002:a1c:f009:: with SMTP id a9mr4513130wmb.158.1601986863174; Tue, 06 Oct 2020 05:21:03 -0700 (PDT) Received: from localhost.localdomain (lns-bzn-59-82-252-130-8.adsl.proxad.net. [82.252.130.8]) by smtp.gmail.com with ESMTPSA id s19sm3742521wmc.41.2020.10.06.05.21.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Oct 2020 05:21:02 -0700 (PDT) From: Daniel Lezcano To: rafael@kernel.org, srinivas.pandruvada@linux.intel.com Cc: lukasz.luba@arm.com, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, rui.zhang@intel.com, "Rafael J. Wysocki" , Arnd Bergmann , linux-arch@vger.kernel.org (open list:GENERIC INCLUDE/ASM HEADER FILES) Subject: [PATCH 3/4] powercap/drivers/dtpm: Add API for dynamic thermal power management Date: Tue, 6 Oct 2020 14:20:23 +0200 Message-Id: <20201006122024.14539-4-daniel.lezcano@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201006122024.14539-1-daniel.lezcano@linaro.org> References: <20201006122024.14539-1-daniel.lezcano@linaro.org> Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org On the embedded world, the complexity of the SoC leads to an increasing number of hotspots which need to be monitored and mitigated as a whole in order to prevent the temperature to go above the normative and legally stated 'skin temperature'. Another aspect is to sustain the performance for a given power budget, for example virtual reality where the user can feel dizziness if the GPU performance is capped while a big CPU is processing something else. Or reduce the battery charging because the dissipated power is too high compared with the power consumed by other devices. The userspace is the most adequate place to dynamically act on the different devices by limiting their power given an application profile: it has the knowledge of the platform. These userspace daemons are in charge of the Dynamic Thermal Power Management (DTPM). Nowadays, the dtpm daemons are abusing the thermal framework as they act on the cooling device state to force a specific and arbitraty state without taking care of the governor decisions. Given the closed loop of some governors that can confuse the logic or directly enter in a decision conflict. As the number of cooling device support is limited today to the CPU and the GPU, the dtpm daemons have little control on the power dissipation of the system. The out of tree solutions are hacking around here and there in the drivers, in the frameworks to have control on the devices. The common solution is to declare them as cooling devices. There is no unification of the power limitation unit, opaque states are used. This patch provides a way to create a hierarchy of constraints using the powercap framework. The devices which are registered as power limit-able devices are represented in this hierarchy as a tree. They are linked together with intermediate nodes which are just there to propagate the constraint to the children. The leaves of the tree are the real devices, the intermediate nodes are virtual, aggregating the children constraints and power characteristics. Each node have a weight on a 2^10 basis, in order to reflect the percentage of power distribution of the children's node. This percentage is used to dispatch the power limit to the children. The weight is computed against the max power of the siblings. This simple approach allows to do a fair distribution of the power limit. Signed-off-by: Daniel Lezcano --- drivers/powercap/Kconfig | 6 + drivers/powercap/Makefile | 1 + drivers/powercap/dtpm.c | 430 ++++++++++++++++++++++++++++++ include/asm-generic/vmlinux.lds.h | 11 + include/linux/dtpm.h | 73 +++++ 5 files changed, 521 insertions(+) create mode 100644 drivers/powercap/dtpm.c create mode 100644 include/linux/dtpm.h -- 2.17.1 Reported-by: kernel test robot Reported-by: kernel test robot Reviewed-by: Lukasz Luba Tested-by: Lukasz Luba diff --git a/drivers/powercap/Kconfig b/drivers/powercap/Kconfig index ebc4d4578339..777cf60300b8 100644 --- a/drivers/powercap/Kconfig +++ b/drivers/powercap/Kconfig @@ -43,4 +43,10 @@ config IDLE_INJECT CPUs for power capping. Idle period can be injected synchronously on a set of specified CPUs or alternatively on a per CPU basis. + +config DTPM + bool "Power capping for dynamic thermal power management" + help + This enables support for the power capping for the dynamic + thermal management userspace engine. endif diff --git a/drivers/powercap/Makefile b/drivers/powercap/Makefile index 7255c94ec61c..6482ac52054d 100644 --- a/drivers/powercap/Makefile +++ b/drivers/powercap/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_DTPM) += dtpm.o obj-$(CONFIG_POWERCAP) += powercap_sys.o obj-$(CONFIG_INTEL_RAPL_CORE) += intel_rapl_common.o obj-$(CONFIG_INTEL_RAPL) += intel_rapl_msr.o diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c new file mode 100644 index 000000000000..6df1e51a2c1c --- /dev/null +++ b/drivers/powercap/dtpm.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Linaro Limited + * + * Author: Daniel Lezcano + * + * The powercap based Dynamic Thermal Power Management framework + * provides to the userspace a consistent API to set the power limit + * on some devices. + * + * DTPM defines the functions to create a tree of constraints. Each + * parent node is a virtual description of the aggregation of the + * children. It propagates the constraints set at its level to its + * children and collect the children power infomation. The leaves of + * the tree are the real devices which have the ability to get their + * current power consumption and set their power limit. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +static const char *constraint_name[] = { + "Instantaneous power constraint", +}; + +static struct powercap_control_type *pct; +static struct dtpm *root; + +static int get_time_window_us(struct powercap_zone *pcz, int cid, u64 *window) +{ + return -ENOSYS; +} + +static int set_time_window_us(struct powercap_zone *pcz, int cid, u64 window) +{ + return -ENOSYS; +} + +static int get_max_power_range_uw(struct powercap_zone *pcz, u64 *max_power_uw) +{ + struct dtpm *dtpm = to_dtpm(pcz); + + spin_lock(&dtpm->lock); + *max_power_uw = dtpm->power_max - dtpm->power_min; + spin_unlock(&dtpm->lock); + + return 0; +} + +static int get_children_power_uw(struct powercap_zone *pcz, u64 *power_uw) +{ + struct dtpm *dtpm = to_dtpm(pcz); + struct dtpm *child; + u64 power; + int ret = 0; + + *power_uw = 0; + + spin_lock(&dtpm->lock); + list_for_each_entry(child, &dtpm->children, sibling) { + ret = child->zone.ops->get_power_uw(&child->zone, &power); + if (ret) + break; + *power_uw += power; + } + spin_unlock(&dtpm->lock); + + return ret; +} + +static void __dtpm_rebalance_weight(struct dtpm *dtpm) +{ + struct dtpm *child; + + spin_lock(&dtpm->lock); + list_for_each_entry(child, &dtpm->children, sibling) { + + pr_debug("Setting weight '%d' for '%s'\n", + child->weight, child->zone.name); + + child->weight = DIV_ROUND_CLOSEST(child->power_max * 1024, + dtpm->power_max); + + __dtpm_rebalance_weight(child); + } + spin_unlock(&dtpm->lock); +} + +static void dtpm_rebalance_weight(void) +{ + __dtpm_rebalance_weight(root); +} + +static void dtpm_sub_power(struct dtpm *dtpm) +{ + struct dtpm *parent = dtpm->parent; + + while (parent) { + spin_lock(&parent->lock); + parent->power_min -= dtpm->power_min; + parent->power_max -= dtpm->power_max; + spin_unlock(&parent->lock); + parent = parent->parent; + } + + dtpm_rebalance_weight(); +} + +static void dtpm_add_power(struct dtpm *dtpm) +{ + struct dtpm *parent = dtpm->parent; + + while (parent) { + spin_lock(&parent->lock); + parent->power_min += dtpm->power_min; + parent->power_max += dtpm->power_max; + spin_unlock(&parent->lock); + parent = parent->parent; + } + + dtpm_rebalance_weight(); +} + +/** + * dtpm_update_power - Update the power on the dtpm + * @dtpm: a pointer to a dtpm structure to update + * @power_min: a u64 representing the new power_min value + * @power_max: a u64 representing the new power_max value + * + * Function to update the power values of the dtpm node specified in + * parameter. These new values will be propagated to the tree. + * + * Return: zero on success, -EINVAL if the values are inconsistent + */ +int dtpm_update_power(struct dtpm *dtpm, u64 power_min, u64 power_max) +{ + if (power_min == dtpm->power_min && power_max == dtpm->power_max) + return 0; + + if (power_max < power_min) + return -EINVAL; + + dtpm_sub_power(dtpm); + spin_lock(&dtpm->lock); + dtpm->power_min = power_min; + dtpm->power_max = power_max; + spin_unlock(&dtpm->lock); + dtpm_add_power(dtpm); + + return 0; +} + +/** + * dtpm_release_zone - Cleanup when the node is released + * @pcz: a pointer to a powercap_zone structure + * + * Do some housecleaning and update the weight on the tree. The + * release will be denied if the node has children. This function must + * be called by the specific release callback of the different + * backends. + * + * Return: 0 on success, -EBUSY if there are children + */ +int dtpm_release_zone(struct powercap_zone *pcz) +{ + struct dtpm *dtpm = to_dtpm(pcz); + struct dtpm *parent = dtpm->parent; + + if (!list_empty(&dtpm->children)) + return -EBUSY; + + if (parent) { + spin_lock(&parent->lock); + list_del(&dtpm->sibling); + spin_unlock(&parent->lock); + } + + dtpm_sub_power(dtpm); + + kfree(dtpm); + + return 0; +} + +/* + * Set the power limit on the nodes, the power limit is distributed + * given the weight of the children. + */ +static int set_children_power_limit(struct powercap_zone *pcz, int cid, + u64 power_limit) +{ + struct dtpm *dtpm = to_dtpm(pcz); + struct dtpm *child; + u64 power; + int ret = 0; + + /* + * Don't allow values outside of the power range previously + * set when initiliazing the power numbers. + */ + power_limit = clamp_val(power_limit, dtpm->power_min, dtpm->power_max); + + spin_lock(&dtpm->lock); + list_for_each_entry(child, &dtpm->children, sibling) { + + /* + * Integer division rounding will inevitably lead to a + * different max value when set several times. In + * order to restore the initial value, we force the + * child's max power every time if the constraint is + * removed by setting a value greater or equal to the + * max power. + */ + if (power_limit == dtpm->power_max) + power = child->power_max; + else + power = DIV_ROUND_CLOSEST( + power_limit * child->weight, 1024); + + pr_debug("Setting power limit for '%s': %llu uW\n", + child->zone.name, power); + + ret = child->zone.constraints->ops->set_power_limit_uw( + &child->zone, cid, power); + if (ret) + break; + } + spin_unlock(&dtpm->lock); + + return ret; +} + +static int get_children_power_limit(struct powercap_zone *pcz, int cid, + u64 *power_limit) +{ + struct dtpm *dtpm = to_dtpm(pcz); + struct dtpm *child; + u64 power; + int ret = 0; + + *power_limit = 0; + + spin_lock(&dtpm->lock); + list_for_each_entry(child, &dtpm->children, sibling) { + ret = child->zone.constraints->ops->get_power_limit_uw( + &child->zone, cid, &power); + if (ret) + break; + *power_limit += power; + } + spin_unlock(&dtpm->lock); + + return ret; +} + +static const char *get_constraint_name(struct powercap_zone *pcz, int cid) +{ + return constraint_name[cid]; +} + +static int get_max_power_uw(struct powercap_zone *pcz, int id, u64 *max_power) +{ + struct dtpm *dtpm = to_dtpm(pcz); + + spin_lock(&dtpm->lock); + *max_power = dtpm->power_max; + spin_unlock(&dtpm->lock); + + return 0; +} + +static struct powercap_zone_constraint_ops constraint_ops = { + .set_power_limit_uw = set_children_power_limit, + .get_power_limit_uw = get_children_power_limit, + .set_time_window_us = set_time_window_us, + .get_time_window_us = get_time_window_us, + .get_max_power_uw = get_max_power_uw, + .get_name = get_constraint_name, +}; + +static struct powercap_zone_ops zone_ops = { + .get_max_power_range_uw = get_max_power_range_uw, + .get_power_uw = get_children_power_uw, + .release = dtpm_release_zone, +}; + +/** + * dtpm_alloc - Allocate and initialize a dtpm struct + * @name: a string specifying the name of the node + * + * Return: a struct dtpm pointer, NULL in case of error + */ +struct dtpm *dtpm_alloc(void) +{ + struct dtpm *dtpm; + + dtpm = kzalloc(sizeof(*dtpm), GFP_KERNEL); + if (dtpm) { + INIT_LIST_HEAD(&dtpm->children); + INIT_LIST_HEAD(&dtpm->sibling); + spin_lock_init(&dtpm->lock); + dtpm->weight = 1024; + } + + return dtpm; +} + +/** + * dtpm_unregister - Unregister a dtpm node from the hierarchy tree + * @dtpm: a pointer to a dtpm structure corresponding to the node to be removed + * + * Call the underlying powercap unregister function. That will call + * the release callback of the powercap zone. + */ +void dtpm_unregister(struct dtpm *dtpm) +{ + powercap_unregister_zone(pct, &dtpm->zone); +} + +/** + * dtpm_register - Register a dtpm node in the hierarchy tree + * @name: a string specifying the name of the node + * @dtpm: a pointer to a dtpm structure corresponding to the new node + * @parent: a pointer to a dtpm structure corresponding to the parent node + * @ops: a pointer to a powercap_zone_ops structure + * @nr_constraints: a integer giving the number of constraints supported + * @const_ops: a pointer to a powercap_zone_constraint_ops structure + * + * Create a dtpm node in the tree. If no parent is specified, the node + * is the root node of the hierarchy. If the root node already exists, + * then the registration will fail. The powercap controller must be + * initialized before calling this function. + * + * The dtpm structure must be initialized with the power numbers + * before calling this function. + * + * Return: zero on success, a negative value in case of error: + * -EAGAIN: the function is called before the framework is initialized. + * -EBUSY: the root node is already inserted + * -EINVAL: there is no root node yet and @parent is specified + * Other negative values are reported back from the powercap framework + */ +int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent, + struct powercap_zone_ops *ops, int nr_constraints, + struct powercap_zone_constraint_ops *const_ops) +{ + struct powercap_zone *pcz; + + if (!pct) + return -EAGAIN; + + if (root && !parent) + return -EBUSY; + + if (!root && parent) + return -EINVAL; + + const_ops->get_name = get_constraint_name; + const_ops->get_max_power_uw = get_max_power_uw; + const_ops->set_time_window_us = set_time_window_us; + const_ops->get_time_window_us = get_time_window_us; + + ops->get_max_power_range_uw = get_max_power_range_uw; + + pcz = powercap_register_zone(&dtpm->zone, pct, name, + parent ? &parent->zone : NULL, + ops, nr_constraints, const_ops); + if (IS_ERR(pcz)) + return PTR_ERR(pcz); + + if (parent) { + spin_lock(&parent->lock); + list_add_tail(&dtpm->sibling, &parent->children); + spin_unlock(&parent->lock); + dtpm->parent = parent; + } else { + root = dtpm; + } + + dtpm_add_power(dtpm); + + return 0; +} + +/** + * dtpm_register_parent - Register a intermediate node in the tree + * @name: a string specifying the name of the node + * @dtpm: a pointer to a dtpm structure corresponding to the new node + * @parent: a pointer to a dtpm structure corresponding parent's new node + * + * The function will add an intermediate node corresponding to a + * parent to more nodes. Its purpose is to aggregate the children + * characteristics and dispatch the constraints. If the @parent + * parameter is NULL, then this node becomes the root node of the tree + * if there is no root node yet. + * + * Return: zero on success, a negative value in case of error: + * -EAGAIN: the function is called before the framework is initialized. + * -EBUSY: the root node is already inserted + * -EINVAL: there is not root node yet and @parent is specified + * Other negative values are reported back from the powercap framework + */ +int dtpm_register_parent(const char *name, struct dtpm *dtpm, + struct dtpm *parent) +{ + return dtpm_register(name, dtpm, parent, &zone_ops, + MAX_DTPM_CONSTRAINTS, &constraint_ops); +} + +static int __init dtpm_init(void) +{ + struct dtpm_descr **dtpm_descr; + + pct = powercap_register_control_type(NULL, "dtpm", NULL); + if (!pct) { + pr_err("Failed to register control type\n"); + return -EINVAL; + } + + for_each_dtpm_table(dtpm_descr) + (*dtpm_descr)->init(*dtpm_descr); + + return 0; +} +late_initcall(dtpm_init); diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 5430febd34be..29b30976ea02 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -315,6 +315,16 @@ #define THERMAL_TABLE(name) #endif +#ifdef CONFIG_DTPM +#define DTPM_TABLE() \ + . = ALIGN(8); \ + __dtpm_table = .; \ + KEEP(*(__dtpm_table)) \ + __dtpm_table_end = .; +#else +#define DTPM_TABLE() +#endif + #define KERNEL_DTB() \ STRUCT_ALIGN(); \ __dtb_start = .; \ @@ -715,6 +725,7 @@ ACPI_PROBE_TABLE(irqchip) \ ACPI_PROBE_TABLE(timer) \ THERMAL_TABLE(governor) \ + DTPM_TABLE() \ EARLYCON_TABLE() \ LSM_TABLE() \ EARLY_LSM_TABLE() diff --git a/include/linux/dtpm.h b/include/linux/dtpm.h new file mode 100644 index 000000000000..6696bdcfdb87 --- /dev/null +++ b/include/linux/dtpm.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 Linaro Ltd + * + * Author: Daniel Lezcano + */ +#ifndef ___DTPM_H__ +#define ___DTPM_H__ + +#include +#include + +#define MAX_DTPM_DESCR 8 +#define MAX_DTPM_CONSTRAINTS 1 + +struct dtpm { + struct powercap_zone zone; + struct dtpm *parent; + struct list_head sibling; + struct list_head children; + spinlock_t lock; + u64 power_limit; + u64 power_max; + u64 power_min; + int weight; + void *private; +}; + +struct dtpm_descr; + +typedef int (*dtpm_init_t)(struct dtpm_descr *); + +struct dtpm_descr { + struct dtpm *parent; + const char *name; + dtpm_init_t init; +}; + +/* Init section thermal table */ +extern struct dtpm_descr *__dtpm_table[]; +extern struct dtpm_descr *__dtpm_table_end[]; + +#define DTPM_TABLE_ENTRY(name) \ + static typeof(name) *__dtpm_table_entry_##name \ + __used __section(__dtpm_table) = &name + +#define DTPM_DECLARE(name) DTPM_TABLE_ENTRY(name) + +#define for_each_dtpm_table(__dtpm) \ + for (__dtpm = __dtpm_table; \ + __dtpm < __dtpm_table_end; \ + __dtpm++) + +static inline struct dtpm *to_dtpm(struct powercap_zone *zone) +{ + return container_of(zone, struct dtpm, zone); +} + +int dtpm_update_power(struct dtpm *dtpm, u64 power_min, u64 power_max); + +int dtpm_release_zone(struct powercap_zone *pcz); + +struct dtpm *dtpm_alloc(void); + +void dtpm_unregister(struct dtpm *dtpm); + +int dtpm_register_parent(const char *name, struct dtpm *dtpm, + struct dtpm *parent); + +int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent, + struct powercap_zone_ops *ops, int nr_constraints, + struct powercap_zone_constraint_ops *const_ops); +#endif From patchwork Tue Oct 6 12:20:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Lezcano X-Patchwork-Id: 286840 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0F8D4C4363D for ; Tue, 6 Oct 2020 12:21:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AA1AD20760 for ; Tue, 6 Oct 2020 12:21:14 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="c6M1Lc+a" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726551AbgJFMVO (ORCPT ); Tue, 6 Oct 2020 08:21:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44178 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726182AbgJFMVN (ORCPT ); Tue, 6 Oct 2020 08:21:13 -0400 Received: from mail-wr1-x444.google.com (mail-wr1-x444.google.com [IPv6:2a00:1450:4864:20::444]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3ECC4C061755 for ; Tue, 6 Oct 2020 05:21:13 -0700 (PDT) Received: by mail-wr1-x444.google.com with SMTP id n15so7464044wrq.2 for ; Tue, 06 Oct 2020 05:21:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=t0N69QCkcOuVUePm6DDLzLJ3FPaHM4LOMBGRQnTz/dA=; b=c6M1Lc+afDx4uhXXLf/SzKWfMa1oMBn+neWQFcy0qgimYoNn3V8OX9CGf3VVdO0nHu hcGjXSMi3vDhg/Kmhz2XI9wZafkHYzgOgduPcBg0yY9ISM06OMitmPHzN0IBvOpec/hL PORQndl1bNHVYio4O5Iyu8Cab0l9+FkOsgcLoIksEacjXQ38EPdEn2u/nqiOIpLTiOnR Qut+1MyXGzgJgQ+QyVA0pziFxtdUWmWBz1L8H2gJY7pDm6m1k/UhM00YsaeDE/0BNagT ZMSrssg0lFzvjTPpjHaGDaoG8mX1PsUQFyOWmy50ZH6cPejf93nbKf7JSfcXezGbwDlJ gEcg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=t0N69QCkcOuVUePm6DDLzLJ3FPaHM4LOMBGRQnTz/dA=; b=h7vlZJw4+k1FyGDznnOg8wZwTX/hAZ27pH8obkBiUuw4tGPW655F28qxd3IblW5myU vPOeHUHtVrP/G5yoUh8m5+WpdqKRJT1iXSAvRDha/oB8KjRh7kziGjOkh5BtdIpVjWSb G+6RMw2VYm/CHjRH8AfPIo+pGRC4XEq7PtcjnubsNvxdnwcIDvZPJpKZT6Y7GmSr2SCQ 1XBsG20I9mYqleuUq9JVbC6liCywR9qf/+sRotbQLP8Ud3x2X80Hu1gS05MeDb9n8nm+ WSiZ07dQUXK7cuqlc82fyC1LvrUl3ue5NpTBjf7ubvaTcIjXWclPxct1X55IjYLoLPbI 39WQ== X-Gm-Message-State: AOAM5329xpimApOrDe05uu2Mn2GKLzCG7WjGPP7mp7K+V2FWKdJoGblD XTc4ZSME3FThljIuVGe/6f0DeQ== X-Google-Smtp-Source: ABdhPJwC89P7MlUrxsd5DJ5eTfNxbp0oV1tGR0OA2BskLMPr8AeOjhS6QyRdMMLfIDKJ9tTZ+YtRBA== X-Received: by 2002:adf:eb04:: with SMTP id s4mr4963161wrn.81.1601986871692; Tue, 06 Oct 2020 05:21:11 -0700 (PDT) Received: from localhost.localdomain (lns-bzn-59-82-252-130-8.adsl.proxad.net. [82.252.130.8]) by smtp.gmail.com with ESMTPSA id s19sm3742521wmc.41.2020.10.06.05.21.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Oct 2020 05:21:11 -0700 (PDT) From: Daniel Lezcano To: rafael@kernel.org, srinivas.pandruvada@linux.intel.com Cc: lukasz.luba@arm.com, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, rui.zhang@intel.com, "Rafael J. Wysocki" , Palmer Dabbelt , Anup Patel , Atish Patra , Marc Zyngier , Andrew Jones , Michael Kelley , Mike Leach , Kajol Jain , Daniel Jordan , Steven Price Subject: [PATCH 4/4] powercap/drivers/dtpm: Add CPU energy model based support Date: Tue, 6 Oct 2020 14:20:24 +0200 Message-Id: <20201006122024.14539-5-daniel.lezcano@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201006122024.14539-1-daniel.lezcano@linaro.org> References: <20201006122024.14539-1-daniel.lezcano@linaro.org> Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org With the powercap dtpm controller, we are able to plug devices with power limitation features in the tree. The following patch introduces the CPU power limitation based on the energy model and the performance states. The power limitation is done at the performance domain level. If some CPUs are unplugged, the corresponding power will be substracted from the performance domain total power. It is up to the platform to initialize the dtpm tree and add the CPU. Here is an example to create a simple tree with one root node called "pkg" and the cpu's performance domains. int dtpm_register_pkg(struct dtpm_descr *descr) { struct dtpm *pkg; int ret; pkg = dtpm_alloc(); if (!pkg) return -ENOMEM; ret = dtpm_register_parent(descr->name, pkg, descr->parent); if (ret) return ret; return dtpm_register_cpu(pkg); } struct dtpm_descr descr = { .name = "pkg", .init = dtpm_register_pkg, }; DTPM_DECLARE(descr); Signed-off-by: Daniel Lezcano --- drivers/powercap/Kconfig | 8 ++ drivers/powercap/Makefile | 1 + drivers/powercap/dtpm_cpu.c | 242 ++++++++++++++++++++++++++++++++++++ include/linux/cpuhotplug.h | 1 + include/linux/dtpm.h | 3 + 5 files changed, 255 insertions(+) create mode 100644 drivers/powercap/dtpm_cpu.c diff --git a/drivers/powercap/Kconfig b/drivers/powercap/Kconfig index 777cf60300b8..240dc09e8dc2 100644 --- a/drivers/powercap/Kconfig +++ b/drivers/powercap/Kconfig @@ -49,4 +49,12 @@ config DTPM help This enables support for the power capping for the dynamic thermal management userspace engine. + +config DTPM_CPU + bool "Add CPU power capping based on the energy model" + depends on DTPM && ENERGY_MODEL + help + This enables support for CPU power limitation based on + energy model. + endif diff --git a/drivers/powercap/Makefile b/drivers/powercap/Makefile index 6482ac52054d..fabcf388a8d3 100644 --- a/drivers/powercap/Makefile +++ b/drivers/powercap/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_DTPM) += dtpm.o +obj-$(CONFIG_DTPM_CPU) += dtpm_cpu.o obj-$(CONFIG_POWERCAP) += powercap_sys.o obj-$(CONFIG_INTEL_RAPL_CORE) += intel_rapl_common.o obj-$(CONFIG_INTEL_RAPL) += intel_rapl_msr.o diff --git a/drivers/powercap/dtpm_cpu.c b/drivers/powercap/dtpm_cpu.c new file mode 100644 index 000000000000..23ebf704c599 --- /dev/null +++ b/drivers/powercap/dtpm_cpu.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Linaro Limited + * + * Author: Daniel Lezcano + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +static struct dtpm *__parent; + +static DEFINE_PER_CPU(struct dtpm *, dtpm_per_cpu); + +struct dtpm_cpu { + struct freq_qos_request qos_req; + int cpu; +}; + +static int power_add(struct dtpm *dtpm, struct em_perf_domain *em) +{ + u64 power_min, power_max; + + power_min = em->table[0].power; + power_min *= MICROWATT_PER_MILLIWATT; + power_min += dtpm->power_min; + + power_max = em->table[em->nr_perf_states - 1].power; + power_max *= MICROWATT_PER_MILLIWATT; + power_max += dtpm->power_max; + + return dtpm_update_power(dtpm, power_min, power_max); +} + +static int power_sub(struct dtpm *dtpm, struct em_perf_domain *em) +{ + u64 power_min, power_max; + + power_min = em->table[0].power; + power_min *= MICROWATT_PER_MILLIWATT; + power_min = dtpm->power_min - power_min; + + power_max = em->table[em->nr_perf_states - 1].power; + power_max *= MICROWATT_PER_MILLIWATT; + power_max = dtpm->power_max - power_max; + + return dtpm_update_power(dtpm, power_min, power_max); +} + +static int set_pd_power_limit(struct powercap_zone *pcz, int cid, + u64 power_limit) +{ + struct dtpm *dtpm = to_dtpm(pcz); + struct dtpm_cpu *dtpm_cpu = dtpm->private; + struct em_perf_domain *pd; + unsigned long freq; + int i, nr_cpus; + + spin_lock(&dtpm->lock); + + power_limit = clamp_val(power_limit, dtpm->power_min, dtpm->power_max); + + pd = em_cpu_get(dtpm_cpu->cpu); + + nr_cpus = cpumask_weight(to_cpumask(pd->cpus)); + + for (i = 0; i < pd->nr_perf_states; i++) { + + u64 power = pd->table[i].power * MICROWATT_PER_MILLIWATT; + + if ((power * nr_cpus) > power_limit) + break; + } + + freq = pd->table[i - 1].frequency; + + freq_qos_update_request(&dtpm_cpu->qos_req, freq); + + dtpm->power_limit = power_limit; + + spin_unlock(&dtpm->lock); + + return 0; +} + +static int get_pd_power_limit(struct powercap_zone *pcz, int cid, u64 *data) +{ + struct dtpm *dtpm = to_dtpm(pcz); + + spin_lock(&dtpm->lock); + *data = dtpm->power_max; + spin_unlock(&dtpm->lock); + + return 0; +} + +static int get_pd_power_uw(struct powercap_zone *pcz, u64 *power_uw) +{ + struct dtpm *dtpm = to_dtpm(pcz); + struct dtpm_cpu *dtpm_cpu = dtpm->private; + struct em_perf_domain *pd; + unsigned long freq; + int i, nr_cpus; + + freq = cpufreq_quick_get(dtpm_cpu->cpu); + pd = em_cpu_get(dtpm_cpu->cpu); + nr_cpus = cpumask_weight(to_cpumask(pd->cpus)); + + for (i = 0; i < pd->nr_perf_states; i++) { + + if (pd->table[i].frequency < freq) + continue; + + *power_uw = pd->table[i].power * + MICROWATT_PER_MILLIWATT * nr_cpus; + + return 0; + } + + return -EINVAL; +} + +static int cpu_release_zone(struct powercap_zone *pcz) +{ + struct dtpm *dtpm = to_dtpm(pcz); + struct dtpm_cpu *dtpm_cpu = dtpm->private; + + freq_qos_remove_request(&dtpm_cpu->qos_req); + + return dtpm_release_zone(pcz); +} + +static struct powercap_zone_constraint_ops pd_constraint_ops = { + .set_power_limit_uw = set_pd_power_limit, + .get_power_limit_uw = get_pd_power_limit, +}; + +static struct powercap_zone_ops pd_zone_ops = { + .get_power_uw = get_pd_power_uw, + .release = cpu_release_zone, +}; + +static int cpuhp_dtpm_cpu_offline(unsigned int cpu) +{ + struct cpufreq_policy *policy; + struct em_perf_domain *pd; + struct dtpm *dtpm; + + policy = cpufreq_cpu_get(cpu); + + if (!policy) + return 0; + + pd = em_cpu_get(cpu); + if (!pd) + return -EINVAL; + + dtpm = per_cpu(dtpm_per_cpu, cpu); + + power_sub(dtpm, pd); + + if (cpumask_weight(policy->cpus) != 1) + return 0; + + for_each_cpu(cpu, policy->related_cpus) + per_cpu(dtpm_per_cpu, cpu) = NULL; + + dtpm_unregister(dtpm); + + return 0; +} + +static int cpuhp_dtpm_cpu_online(unsigned int cpu) +{ + struct dtpm *dtpm; + struct dtpm_cpu *dtpm_cpu; + struct cpufreq_policy *policy; + struct em_perf_domain *pd; + char name[CPUFREQ_NAME_LEN]; + int ret; + + policy = cpufreq_cpu_get(cpu); + + if (!policy) + return 0; + + pd = em_cpu_get(cpu); + if (!pd) + return -EINVAL; + + dtpm = per_cpu(dtpm_per_cpu, cpu); + if (dtpm) + return power_add(dtpm, pd); + + dtpm = dtpm_alloc(); + if (!dtpm) + return -EINVAL; + + dtpm_cpu = kzalloc(sizeof(dtpm_cpu), GFP_KERNEL); + if (!dtpm_cpu) + return -ENOMEM; + + dtpm->private = dtpm_cpu; + dtpm_cpu->cpu = cpu; + + for_each_cpu(cpu, policy->related_cpus) + per_cpu(dtpm_per_cpu, cpu) = dtpm; + + ret = power_add(dtpm, pd); + if (ret) + return ret; + + dtpm->power_limit = dtpm->power_max; + + sprintf(name, "cpu%d", dtpm_cpu->cpu); + + ret = dtpm_register(name, dtpm, __parent, &pd_zone_ops, + 1, &pd_constraint_ops); + if (ret) + return ret; + + ret = freq_qos_add_request(&policy->constraints, + &dtpm_cpu->qos_req, FREQ_QOS_MAX, + pd->table[pd->nr_perf_states - 1].frequency); + return ret; +} + +int dtpm_register_cpu(struct dtpm *parent) +{ + __parent = parent; + + return cpuhp_setup_state(CPUHP_AP_DTPM_CPU_ONLINE, + "dtpm_cpu:online", + cpuhp_dtpm_cpu_online, + cpuhp_dtpm_cpu_offline); +} diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index bf9181cef444..6792bad4a435 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -190,6 +190,7 @@ enum cpuhp_state { CPUHP_AP_ONLINE_DYN_END = CPUHP_AP_ONLINE_DYN + 30, CPUHP_AP_X86_HPET_ONLINE, CPUHP_AP_X86_KVM_CLK_ONLINE, + CPUHP_AP_DTPM_CPU_ONLINE, CPUHP_AP_ACTIVE, CPUHP_ONLINE, }; diff --git a/include/linux/dtpm.h b/include/linux/dtpm.h index 6696bdcfdb87..b62215a13baa 100644 --- a/include/linux/dtpm.h +++ b/include/linux/dtpm.h @@ -70,4 +70,7 @@ int dtpm_register_parent(const char *name, struct dtpm *dtpm, int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent, struct powercap_zone_ops *ops, int nr_constraints, struct powercap_zone_constraint_ops *const_ops); + +int dtpm_register_cpu(struct dtpm *parent); + #endif