From nobody Fri Oct 2 01:12:19 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8870A448B8D for ; Thu, 6 Aug 2026 11:02:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786014141; cv=none; b=CaYxVKvKNYaxmXypL68nF+SwX7R2imaGz+DCAHk/DZ5I6g5S7DMpZZ/3QTqvIc1Lmb2xtA3yDnNdkpUzk7NpLQei1ZpJRiZ78OxALvuPgPquMJm7OzHfPL1uR5dInfPL2CopA7o1DWuHYhnsAfw3YCU50WkZ5hEGnJj2aPwhgaw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786014141; c=relaxed/simple; bh=r8Gnr1+T9r7J2+wuhx0yw3OuTJgTw0C5QxW83obdyic=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tYBYJUCJShXIHV8C4Nqv54ucmErY2ll5zLMG1csPQ5/3Ql2qjdhWMgc4EYjfgMXy8FvV52JY8GjNyyNeeGPMX12NbqbcrYKl0Tv6CDR1HpnXk4h0NWPpYx0kzmURJkVdjjfXvdtgvJ23hX6qBn+3dQtKA8ZFb9ueNNtNWOq8Je8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=pgJ8mGu3; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=NdtVcsLd; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="pgJ8mGu3"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="NdtVcsLd" Received: from pps.filterd (m0279866.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 6769nkGv1228244 for ; Thu, 6 Aug 2026 11:02:18 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=JpV5+FuyfSk 2NiHdf+Bry5eOP6QolE5Yf8x3iNNSqIA=; b=pgJ8mGu35O0r7cDOANuk5PfjhFn NWKTqhEoaujEVM0DeVik7Vl9PwVHC9b202I6CSNHDaZbFXtzFD+NkH3mSQRLU/Le gGxzFE4Bkh5zQ/SSOURAtazLsma0/h8hptr4JT1kzqTe65zaoJIyx1PNE7Vocj1p jejXjT1lW0EJHygY+IWjhD6me+iEZbaYl7xy45mZizKwpONwYFmUXbG6Uvens7s2 uAemE34PkBu2joz3HlnjVfntfSCZ63K94zYIJZsFDSICuWVrMfltPnJwkMHxkhw6 a+jG5vcvywbwMtNi+aqjNJ6pjphM75/XmnZBVGNJU7c+Ns9l9XjoL7UPzgA== Received: from mail-yw1-f199.google.com (mail-yw1-f199.google.com [209.85.128.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fvqduge51-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Thu, 06 Aug 2026 11:02:18 +0000 (GMT) Received: by mail-yw1-f199.google.com with SMTP id 00721157ae682-81aea53d559so38906477b3.0 for ; Thu, 06 Aug 2026 04:02:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1786014138; x=1786618938; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=JpV5+FuyfSk2NiHdf+Bry5eOP6QolE5Yf8x3iNNSqIA=; b=NdtVcsLd783fPlds6GQiMeeli4xRos/ImpVjhFor0a6xzMPmHcQvhxkeQTSsZs61oh bj+58ZuGlmXBZIXOS3vSX1LmSUfbZYRWnd7s5FqRtUtszvNzS+E93HjRuBTGnU8K9z+q aiV9tHISb9A2mqKTQEuo8dG+lH2WJE1YbZHF1Zf1R7EqbJeE8Ul6NytZQUOaHNBAiOHO M/HCO1N2zxRuWQYQn2FahtREAKrARK4iLMXuU94f3QMBCeRZ1hPmeCwbeCpe9eKigPiR d/u3BWvkDsLskOUmE5A9kzS+psXiHViNiOmfYTRQ0Uy8mJM7cO1v6ubasJG26wnLIuVl 4zMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786014138; x=1786618938; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=JpV5+FuyfSk2NiHdf+Bry5eOP6QolE5Yf8x3iNNSqIA=; b=OeHVfcClH1v2+DtWe8S2mpMJG0x9lv1ZlH6P+QaedKsY6ALnKTgfOJpOPlDLWuZTzP kHdd51Q6usPu3cK6HVS7oBmAbARrucQQUDD1iCEFP6YQsdOBlL6iwl5INv2gB/a1dpnJ O4pQuNNuI9FZ0XkTstmVEZC4rKxwtNxoOeJscezdJVT9V0J15OjokNp/a7qous/pgIWn yrx/NajmMiFRmklcBcdSr5DayHLxetsY3qMroxX9Tfw0NjiBBlC4bURKzxqT/2+d0RmW lSOtgHfurjDBssy0izlKqRuYwpqK34kMeR1wYz99O1BhOeSWzcKAmWW+Rbs4IkR+2yaN 3MEw== X-Forwarded-Encrypted: i=1; AHgh+RqCYqR4ia+F9IAmoW7NogJk/2G6j480fJqo6YrwHf4ldLFNhE+MGHUR7Q6Osd9hm2eJ0BW+r5DUdL6J1os=@vger.kernel.org X-Gm-Message-State: AOJu0YwTZP/1Lyfeq82084t8dR9Fg8m1VHOGR6xY+qd7dN4NL21ovSN+ 22kpAJzn9Q6i8Jb9HeD8kCZ6dSPOcvw7k/6BPU5uW7rzywE7OGaivei3YurIdfTzveZ0DDC662F /BxjiWSybL286sA6dEbWPRo1HAUzXZiwzCaxGoVyafaZ9KY1Zgz4o3ZA5YDxXcnJOIKeCI9fjn7 RXRxw8 X-Gm-Gg: AR+sD10FJFfZsgLjoWIqeLo/uuwVhB0XzeV0dd94gUHfreZFUTiza1jHt6PuSlk1ixj QIR74xjKXErI6tX7DuQ0h1ozGIghvszXi++d0b/WtOLnBLrFBk6URZpRfmIS6+O9fQoQ4GAspij 13VOThf87UJX+OKSYXLMlSDctvGEIfn/ljLkDoasn7GHngHGAV7BrYNWKs+NvLqqWFoXB8Tq3P8 C+ZbHb3rc8IvO0Tgktm4/r58LnVWcmjg9gNMKSofDxoVicPqL55dM2u/UlAi9I3HYIAT0jPl94p U3oc3kXN16MfnrA98gTy/fHSh4XaPcSEbrppy8c6KhGtlfFyVhLZjmVwqIzQQOHamm0hhgI97bj /T6/JyLjmj7Dtbwam6Pg= X-Received: by 2002:a05:690c:b85:b0:81e:6cd1:5329 with SMTP id 00721157ae682-8202275ac60mr82831057b3.34.1786014137606; Thu, 06 Aug 2026 04:02:17 -0700 (PDT) X-Received: by 2002:a05:690c:b85:b0:81e:6cd1:5329 with SMTP id 00721157ae682-8202275ac60mr82830557b3.34.1786014136982; Thu, 06 Aug 2026 04:02:16 -0700 (PDT) Received: from juillet.lan ([2a01:cde0:108:24ed:1501:7670:a13c:4834]) by smtp.gmail.com with ESMTPSA id 00721157ae682-820131db335sm36808917b3.3.2026.08.06.04.02.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2026 04:02:16 -0700 (PDT) From: Daniel Lezcano To: rafael@kernel.org Cc: linux-pm@vger.kernel.org, shuah@kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, manaf.pallikunhi@oss.qualcomm.com Subject: [PATCH 1/2] powercap: Add generic zone hierarchy creation helpers Date: Thu, 6 Aug 2026 13:01:58 +0200 Message-ID: <20260806110159.69690-2-daniel.lezcano@oss.qualcomm.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260806110159.69690-1-daniel.lezcano@oss.qualcomm.com> References: <20260806110159.69690-1-daniel.lezcano@oss.qualcomm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-GUID: KN0PIxWR32fSrHuAIibAZ6dtEY8kdvYo X-Proofpoint-ORIG-GUID: KN0PIxWR32fSrHuAIibAZ6dtEY8kdvYo X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwODA2MDA4NyBTYWx0ZWRfXztyoTmVFl4Mi 9PpM6pBPmjcHvIWvDfFScOq090uE2IY4tlDv7oOFsdAI5foeZ4y3hX3nlScUHO+VD50Gl0kItTU 4zV742mcT+y/6V27zYCZQM0Z7KmZfFkqvjaQzgDEAzGzXJydTzDSqg5cJJbsX/jyMQ/Auxmq4Vu Vi+o2n55dbzMZT/qZ7NUaGq+omSHiU6hyuoZEnODJynf4KT6FQ9rUH5jIHdDfFW7uZKfC4kJVbZ B2GPHsf/CwjetzFBbWEjcSKuvZJvQxYUaho+U15dzm+hKKI9iScIggUeTLUZbiDWxrnh2NMraYd +/ApUbLdw0vQbnaN5erXS2Kug+ZZH2pnwhRIknI39YfUyrYflMA9olgy3DeI9gbUhf0/OCYFzen aGLUbhh1ay6RL3V11g+aJQBLSBu3fdE4v7UaPUXOBLAMz9x8ygK1vkbLuMPLoPOSAtM5BuN88Na w9sJK8IGH84QKYFBX9Q== X-Proofpoint-Spam-Info: AW1haW4tMjYwODA2MDA4NyBTYWx0ZWRfX/t+OWm98+Z6B rp/pqs/+EiLO5go/Bk/uXTOOp53P2ABbNTAR2OVBVSwSkXxmtJ0xu0zGnaGU5utDp6rm4YeDC8U Q7ItqRVH0qnYUqyNY0bOn1boeV7mv/I= X-Authority-Analysis: v=2.4 cv=bdtbluPB c=1 sm=1 tr=0 ts=6a7469ba cx=c_pps a=72HoHk1woDtn7btP4rdmlg==:117 a=xqWC_Br6kY4A:10 a=Sv0fKeRqtYgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=YMgV9FUhrdKAYTUUvYB2:22 a=EUspDBNiAAAA:8 a=jGdnt-AriNhNVhWiu9AA:9 a=kA6IBgd4cpdPkAWqgNAz:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-08-05_06,2026-08-05_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 phishscore=0 clxscore=1015 spamscore=0 bulkscore=0 malwarescore=0 suspectscore=0 lowpriorityscore=0 priorityscore=1501 adultscore=0 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2608060087 Content-Type: text/plain; charset="utf-8" Powercap controllers may need to create several powercap zones organized as a hierarchy. At present, each controller has to open-code the hierarchy traversal, parent lookup, error rollback and reverse-order destruction. Introduce struct powercap_hierarchy to describe a powercap hierarchy. Each node contains its name, parent, backend-specific data and the powercap zone created for it. For example, the following description: static struct powercap_node nodes[] =3D { { .name =3D "package" }, { .name =3D "cpu", .parent =3D &nodes[0] }, { .name =3D "gpu", .parent =3D &nodes[0] }, }; static struct powercap_hierarchy hierarchy =3D { .nodes =3D nodes, .nr_nodes =3D ARRAY_SIZE(nodes), }; creates the following hierarchy: package |-- cpu `-- gpu Add powercap_hierarchy_dup() to create a runtime copy of a hierarchy description. Rebase the parent pointers so that the copy does not keep references to the original array, which may be stored in init memory. Add powercap_hierarchy_create() to walk the description in order and create each zone through a controller-provided callback. The parent zone is passed to the callback, keeping the node creation operation specific to the controller. The backend is responsible for allocating and registering each powercap zone from the creation callback. The powercap_zone object is expected to be embedded in a backend-specific structure, allowing the backend callbacks to retrieve their private data later using container_of(). For example: struct foo_powercap_zone { struct powercap_zone zone; struct foo_domain *domain; }; foo_zone =3D kzalloc(sizeof(*foo_zone), GFP_KERNEL); if (!foo_zone) return ERR_PTR(-ENOMEM); foo_zone->domain =3D domain; pcz =3D powercap_register_zone(&foo_zone->zone, pct, name, parent, &foo_zone_ops, nr_constraints, &foo_constraint_ops); Its allocation, private state and lifetime remain under the control of the backend. The creation callback therefore returns the same powercap_zone pointer that was passed to powercap_register_zone(). The hierarchy helper stores this pointer to provide it as the parent of subsequent nodes and to pass it back to the backend during hierarchy destruction. Add powercap_hierarchy_destroy() to destroy the hierarchy in reverse order, ensuring that children are removed before their parents. Use the same mechanism to roll back previously created zones when the creation of a subsequent node fails. Serialize creation and destruction of each hierarchy to prevent concurrent updates of the powercap zone pointers stored in its runtime copy. This provides a common mechanism for creating controller-defined powercap hierarchies while keeping controller-specific operations outside the powercap core. Cc: Manaf Meethalavalappu Pallikunhi Signed-off-by: Daniel Lezcano Tested-by: Manaf Meethalavalappu Pallikunhi=20 --- drivers/powercap/powercap_sys.c | 148 ++++++++++++++++++++++++++ include/linux/powercap.h | 178 ++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+) diff --git a/drivers/powercap/powercap_sys.c b/drivers/powercap/powercap_sy= s.c index 9197fa20d93f..fefb36c5bcd2 100644 --- a/drivers/powercap/powercap_sys.c +++ b/drivers/powercap/powercap_sys.c @@ -667,6 +667,154 @@ int powercap_unregister_control_type(struct powercap_= control_type *control_type) } EXPORT_SYMBOL_GPL(powercap_unregister_control_type); =20 +struct powercap_hierarchy * +powercap_hierarchy_dup(const struct powercap_hierarchy *hierarchy) +{ + struct powercap_hierarchy *copy; + size_t i; + + if (!hierarchy || !hierarchy->nodes || !hierarchy->nr_nodes) + return ERR_PTR(-EINVAL); + + copy =3D kzalloc_obj(*copy); + if (!copy) + return ERR_PTR(-ENOMEM); + + /* + * The source node array may live in init memory. A shallow copy would + * leave parent pointers referencing that array after it has been freed. + * Copy the nodes now and rebase their parent pointers below. The name + * and data objects remain owned by the backend and are not duplicated. + */ + copy->nodes =3D kmemdup_array(hierarchy->nodes, hierarchy->nr_nodes, + sizeof(*copy->nodes), GFP_KERNEL); + if (!copy->nodes) { + kfree(copy); + return ERR_PTR(-ENOMEM); + } + + copy->nr_nodes =3D hierarchy->nr_nodes; + mutex_init(©->lock); + + for (i =3D 0; i < copy->nr_nodes; i++) { + const struct powercap_node *parent =3D hierarchy->nodes[i].parent; + ptrdiff_t index; + + copy->nodes[i].pcz =3D NULL; + + if (!copy->nodes[i].name) + goto invalid; + + if (!parent) + continue; + + index =3D parent - hierarchy->nodes; + if (index < 0 || index >=3D i) + goto invalid; + + copy->nodes[i].parent =3D ©->nodes[index]; + } + + return copy; + +invalid: + mutex_destroy(©->lock); + kfree(copy->nodes); + kfree(copy); + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(powercap_hierarchy_dup); + +void powercap_hierarchy_free(struct powercap_hierarchy *hierarchy) +{ + if (!hierarchy) + return; + + mutex_destroy(&hierarchy->lock); + kfree(hierarchy->nodes); + kfree(hierarchy); +} +EXPORT_SYMBOL_GPL(powercap_hierarchy_free); + +static void __powercap_hierarchy_destroy(struct powercap_control_type *pct, + struct powercap_hierarchy *hierarchy, + powercap_node_destroy_t powercap_node_destroy) +{ + size_t i; + + for (i =3D hierarchy->nr_nodes; i-- > 0;) { + if (!hierarchy->nodes[i].pcz) + continue; + + powercap_node_destroy(pct, hierarchy->nodes[i].pcz, + hierarchy->nodes[i].data); + + hierarchy->nodes[i].pcz =3D NULL; + } +} + +int powercap_hierarchy_destroy(struct powercap_control_type *pct, + struct powercap_hierarchy *hierarchy, + powercap_node_destroy_t powercap_node_destroy) +{ + if (!pct || !hierarchy || !hierarchy->nodes || + !hierarchy->nr_nodes || !powercap_node_destroy) + return -EINVAL; + + guard(mutex)(&hierarchy->lock); + + __powercap_hierarchy_destroy(pct, hierarchy, powercap_node_destroy); + + return 0; +} +EXPORT_SYMBOL_GPL(powercap_hierarchy_destroy); + +int powercap_hierarchy_create(struct powercap_control_type *pct, + struct powercap_hierarchy *hierarchy, + powercap_node_create_t powercap_node_create, + powercap_node_destroy_t powercap_node_destroy) +{ + struct powercap_zone *pcz; + int ret; + size_t i; + + if (!pct || !hierarchy || !hierarchy->nodes || !hierarchy->nr_nodes || + !powercap_node_create || !powercap_node_destroy) + return -EINVAL; + + guard(mutex)(&hierarchy->lock); + + for (i =3D 0; i < hierarchy->nr_nodes; i++) { + struct powercap_zone *parent =3D NULL; + + if (hierarchy->nodes[i].parent) { + parent =3D hierarchy->nodes[i].parent->pcz; + if (!parent) { + ret =3D -EINVAL; + goto rollback; + } + } + + pcz =3D powercap_node_create(pct, hierarchy->nodes[i].name, + hierarchy->nodes[i].data, parent); + if (IS_ERR_OR_NULL(pcz)) { + ret =3D pcz ? PTR_ERR(pcz) : -EINVAL; + goto rollback; + } + + hierarchy->nodes[i].pcz =3D pcz; + } + + return 0; + +rollback: + __powercap_hierarchy_destroy(pct, hierarchy, powercap_node_destroy); + + return ret; +} +EXPORT_SYMBOL_GPL(powercap_hierarchy_create); + static int __init powercap_init(void) { int result; diff --git a/include/linux/powercap.h b/include/linux/powercap.h index 603419db924c..939cd58dff4f 100644 --- a/include/linux/powercap.h +++ b/include/linux/powercap.h @@ -9,6 +9,7 @@ =20 #include #include +#include =20 /* * A power cap class device can contain multiple powercap control_types. @@ -309,4 +310,181 @@ struct powercap_zone *powercap_register_zone( int powercap_unregister_zone(struct powercap_control_type *control_type, struct powercap_zone *power_zone); =20 +/** + * struct powercap_node - Description of a node in a powercap hierarchy + * @name: Name of the powercap zone. + * @parent: Parent node, or NULL if the node is a hierarchy root. + * @pcz: Powercap zone created for this node. This field is managed by the + * hierarchy creation and destruction helpers. + * @data: Private data passed unchanged to the creation and destruction + * callbacks. + * + * This structure describes one node of a powercap hierarchy. The backend + * supplies an array of nodes through &struct powercap_hierarchy. + * + * Nodes must be ordered so that a parent appears before all its children. + * + * The @name and @data objects must remain valid until the duplicated hier= archy + * has been freed. The @parent pointer is rebased by powercap_hierarchy_du= p(), + * allowing the original node array to be released after duplication. + * + * The @pcz field is runtime state managed by powercap_hierarchy_create() = and + * powercap_hierarchy_destroy(). It is cleared when a node is destroyed. + */ +struct powercap_node { + const char *name; + struct powercap_node *parent; + struct powercap_zone *pcz; + void *data; +}; + +/** + * struct powercap_hierarchy - Powercap zone hierarchy + * @nodes: Array describing the hierarchy nodes. + * @nr_nodes: Number of entries in @nodes. + * @lock: Lock serializing hierarchy creation and destruction. + * + * A backend may place the initial description in init memory and duplicat= e it + * with powercap_hierarchy_dup() before the init sections are released. The + * duplicated hierarchy owns the @nodes array, but not the objects referen= ced + * by &struct powercap_node.name and &struct powercap_node.data. + */ +struct powercap_hierarchy { + struct powercap_node *nodes; + size_t nr_nodes; + struct mutex lock; +}; + +/** + * powercap_hierarchy_dup - Duplicate a powercap hierarchy description + * @hierarchy: Hierarchy description to duplicate. + * + * Allocate a runtime hierarchy and copy the nodes from @hierarchy. Parent + * pointers are rebased to the duplicated node array and the runtime pcz f= ields + * are initialized to NULL. + * + * Every parent must belong to the source node array and precede its child= ren. + * The node names and private data are not duplicated and must remain valid + * until powercap_hierarchy_free() is called. + * + * Context: Process context. May sleep. + * + * Return: A pointer to the duplicated hierarchy on success, or an ERR_PTR= () + * encoded error otherwise. + */ +struct powercap_hierarchy *powercap_hierarchy_dup(const struct powercap_hi= erarchy *hierarchy); + +/** + * powercap_hierarchy_free - Free a duplicated powercap hierarchy + * @hierarchy: Hierarchy to free, or NULL. + * + * Free the node array and hierarchy allocated by powercap_hierarchy_dup()= . All + * registered zones must have been destroyed before calling this function. + */ +void powercap_hierarchy_free(struct powercap_hierarchy *hierarchy); + +/** + * typedef powercap_node_create_t - Create a powercap hierarchy node + * @pct: Powercap control type owning the hierarchy. + * @name: Name of the powercap zone to create. + * @data: Private data associated with the hierarchy node. + * @parent: Parent powercap zone, or NULL for a root node. + * + * Callback invoked by powercap_hierarchy_create() for each node in the + * hierarchy. The callback must create and register a powercap zone below + * @parent. The backend is expected to embed struct powercap_zone in its o= wn + * object, pass the address of that member to powercap_register_zone(), and + * return the same address from this callback. This lets the backend recov= er + * its object with container_of() from subsequent powercap callbacks. + * + * Context: Called with the powercap hierarchy mutex held. The callback may + * sleep, but must not call powercap_hierarchy_create() or + * powercap_hierarchy_destroy() for the same hierarchy. + * + * Return: A valid pointer to the created powercap zone on success, or an + * ERR_PTR() encoded error on failure. + */ +typedef struct powercap_zone *(*powercap_node_create_t)(struct powercap_co= ntrol_type *pct, + const char *name, void *data, + struct powercap_zone *parent); +/** + * typedef powercap_node_destroy_t - Destroy a powercap hierarchy node + * @pct: Powercap control type owning the hierarchy. + * @zone: Powercap zone to destroy. + * @data: Private data associated with the hierarchy node. + * + * Callback invoked when a hierarchy is destroyed or when its creation must + * be rolled back. The callback must unregister the powercap zone represen= ted + * by @zone. The backend remains responsible for the lifetime of the enclo= sing + * object, including releasing it from the powercap zone release callback = when + * necessary. + * + * Nodes are passed to this callback in reverse creation order, ensuring t= hat + * all children are destroyed before their parent. + * + * Context: Called with the powercap hierarchy mutex held. The callback may + * sleep, but must not call powercap_hierarchy_create() or + * powercap_hierarchy_destroy() for the same hierarchy. + */ +typedef void (*powercap_node_destroy_t)(struct powercap_control_type *pct, + struct powercap_zone *zone, + void *data); + +/** + * powercap_hierarchy_destroy - Destroy a powercap hierarchy + * @pct: Powercap control type owning the hierarchy. + * @hierarchy: Hierarchy to destroy. + * @powercap_node_destroy: Callback used to destroy each powercap zone. + * + * Destroy all powercap zones previously created for @hierarchy. Nodes are + * destroyed in reverse array order so that children are removed before th= eir + * parents. + * + * Entries whose &struct powercap_node.pcz field is NULL are ignored. After + * a zone has been destroyed, its pcz field is cleared. + * + * The caller must ensure that no users of the hierarchy remain when this + * function is called. + * + * Context: Process context. May sleep. + * + * Return: 0 on success or -EINVAL if an argument is invalid. + */ +int powercap_hierarchy_destroy(struct powercap_control_type *pct, + struct powercap_hierarchy *hierarchy, + powercap_node_destroy_t powercap_node_destroy); + +/** + * powercap_hierarchy_create - Create a powercap hierarchy + * @pct: Powercap control type that will own the hierarchy. + * @hierarchy: Hierarchy to create. + * @powercap_node_create: Callback used to create each powercap zone. + * @powercap_node_destroy: Callback used to roll back an incomplete hierar= chy. + * + * Create the powercap zones described by @hierarchy in array order. For e= ach + * entry, @powercap_node_create is called with the powercap zone stored in= its + * parent entry. Root nodes are created with a NULL parent. + * + * Each parent entry must precede all its children in the node array. All = pcz + * fields must be NULL when this function is called. + * + * If a node cannot be created, all zones created by this invocation are + * destroyed in reverse order by calling @powercap_node_destroy. Therefore, + * @powercap_node_destroy must be provided even when the caller does not + * expect to destroy the hierarchy explicitly. + * + * The hierarchy and the objects referenced by its name and data fields mu= st + * remain valid until powercap_hierarchy_destroy() has completed. + * + * Context: Process context. May sleep. + * + * Return: 0 on success, -EINVAL if an argument or hierarchy entry is inva= lid, + * -EBUSY if the hierarchy already contains a created zone, or the error + * returned by @powercap_node_create. + */ +int powercap_hierarchy_create(struct powercap_control_type *pct, + struct powercap_hierarchy *hierarchy, + powercap_node_create_t powercap_node_create, + powercap_node_destroy_t powercap_node_destroy); + #endif --=20 2.53.0 From nobody Fri Oct 2 01:12:19 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DEF5344C4E8 for ; Thu, 6 Aug 2026 11:02:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786014143; cv=none; b=M1j8qdwSUkxHpkYEWF9VyS+UE534yqskUNq1VYWHZfmm7RnauZLSsE7WuKn7M0B3B9OYhRCb5P0+7lHey298UElGr80ZKmV/xdN15iKK6V1qccQcqGgovZc8LfPnwF7T4cpsMmekZy8WAa0NZy0kU3dsJ+z9zjeDIqadhOKIqsU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786014143; c=relaxed/simple; bh=8lt42/buAHFphD/X2Ubb5FOVWRvH0w8Fo9mbigYmOzg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OjLORxvky6wf+EDbBM2xB8awTRkz0K9wtwD+Nplrq0fN2bXvROItErj/nBaq0qds3wop7EKVEChJX3lDPjGb/Vx1XOvnA9FM0S1jVY32x5qGAdy40isK1vXQgXANItSCPuXCUksTHD/VI+FdXwYSeTHfZr7Ugz/cfXkJqL5msvU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=RvXhU4m3; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=WUHPnysC; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="RvXhU4m3"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="WUHPnysC" Received: from pps.filterd (m0279866.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 6769nkGw1228244 for ; Thu, 6 Aug 2026 11:02:20 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=OQ2LB/2wLJ9 xLA2vO6RiQF0IzMe+r6MJZcfrmV+gTOg=; b=RvXhU4m3/0kALrH0zjqfvUiOl8a rD3fMYbsUQuOUqz0clSaoAcxIY1VucRyjvytMx/ak1KZV7kUOTWPOqLOZP6SfmV/ oMugcgRozjN5KRHVuformVW2vInx8DbWe3ZJLw4LxeaLjZ9kfb9OI4Z+DtrSHMoV VT9eKjsHwTgrVA5Lf2D5kr/15gOA33xD9aFPf39EBInxIRf1c+r+y+W8U9mxw67l /WtKYvV3y32YYDERi7/Ku+AMYNZfAD4NXXAX77bwrQdEt8L42Mw6Ue8TRSRtgBJv 0iH3sRo2dcG926ONPOlqjliHsCQ72rlgCJOhju7m96uzzi9kC9lajl1HxxQ== Received: from mail-yw1-f199.google.com (mail-yw1-f199.google.com [209.85.128.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fvqduge5p-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Thu, 06 Aug 2026 11:02:19 +0000 (GMT) Received: by mail-yw1-f199.google.com with SMTP id 00721157ae682-81eddfe28b4so56576707b3.3 for ; Thu, 06 Aug 2026 04:02:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1786014139; x=1786618939; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=OQ2LB/2wLJ9xLA2vO6RiQF0IzMe+r6MJZcfrmV+gTOg=; b=WUHPnysCaGADE3APCSnEBYdk/Ii6vciaM2Eixdxo3O0leEmb3FeaSjDq4vJi5FMdU2 w8dz8eF7NEnd8Pg60xiImOKGw86EaDODhdzGDYkxquBa1wgl6HuLBacX/FO+GrpJG8p8 RrnqVRlpzexP+BZUSdgH8v3/nqV7/tOlUuVIaj548eYibSN280h4qxiyaPjLSIBnTbwY fw3onX2TeigPma8NyuYrocMlWtQpq/Pe1aUeSrpnVGQGZBxTzX4OfctgmO2WM2ReoQHP ZoDs+HznD4h2v+N/cFxXjSo7sljY4jiYtr/M4OSgM7cl89HEzrkvY8svR3dTpW+inDIU SW3A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786014139; x=1786618939; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=OQ2LB/2wLJ9xLA2vO6RiQF0IzMe+r6MJZcfrmV+gTOg=; b=SpLwCpl/qPZ9GfXkbYGa+jhXwEwi2uq+g1tP8rIdxywTwtSQc5txX//c5v/M+OPFS8 mNNH5T4mN4tiKTKZ9Jnr/lrrGbydUh1HdO9ECQi4kTM4DJtG3+kJC48tnr+OMj/sPLtZ gkMPGBG52TpQL3WoO33eMdNoi7eKUfSPwANIGO9iWFWsj53sEiHPb6juH9ekrT2UV2yK XmzyXO9y6MGcw4k0nK19/UdauXfWxVzaiebaSuE0Hc7OeKj+qOBN0dg/0lpJl10tWVo/ YUzCTIasF+bOkkF7yXymTW3bcAXZpC9tbnj/06XIaj7CBfUN2cgWdHqcsyDVazAuSD2l Ep3g== X-Forwarded-Encrypted: i=1; AHgh+Rrntf3urRbuSN9Eop3sQ5TvgydelGKI5FyxfP1CNWwBL04QOYZtC+COeCZjSeXfNyJ4CEY9U693DC18n08=@vger.kernel.org X-Gm-Message-State: AOJu0Yxtdg0DYbShjctlvSKFwzXsSRZbdDjsprjtx/MVYWYZz7syGXfN VN0lICgRDPuuIXQaAMicxFWp+YBcwaBIeq8e+21IDEJgl9pgHjfL1rHALNPEm5zwqSbzghsfN3w FgLqQC06iJNC4oZThboy9qVoouLoXRBTy3dATpPLr1lsYQsgFJocbUdKAwL3VDu6FFV4= X-Gm-Gg: AR+sD11AQK9X2G/jpSkZ0nKbd853/03a4bEa40zxU75Hq75+rAJntXbSvk0x6w7Xi2C ByvIHdHmZ8PIf2sdS9+ewjE9jR6inD56XPLAUkN9tvM0Bwpepp1ee7NIPHwM58S7qDQiXXURlfD HbsrROshciN4s1diIhAlXzINkKmsTVptvXwg0/2OckNoYJNF3Z8S91Qfy4HUeMgx6tuBwgvK7D6 ky4tDZrKLcVmz+hWwcJyAVou7LjFmE1akFcwFexdn/JLoKP8/qikmqKX24yTRPZb4+NdgIF75Vd WmluZWKgfYpSUq/H8Pk11Ft9LGe5jJrBxjyvPx1c4gDch6u0crosSLz+pNmmKm5F4446yvWxiSj dwNK4BfqfSg6yW4aS5g4= X-Received: by 2002:a05:690c:448c:b0:809:ae5:1d2f with SMTP id 00721157ae682-8201c4895f7mr84040757b3.9.1786014138822; Thu, 06 Aug 2026 04:02:18 -0700 (PDT) X-Received: by 2002:a05:690c:448c:b0:809:ae5:1d2f with SMTP id 00721157ae682-8201c4895f7mr84040527b3.9.1786014138430; Thu, 06 Aug 2026 04:02:18 -0700 (PDT) Received: from juillet.lan ([2a01:cde0:108:24ed:1501:7670:a13c:4834]) by smtp.gmail.com with ESMTPSA id 00721157ae682-820131db335sm36808917b3.3.2026.08.06.04.02.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2026 04:02:17 -0700 (PDT) From: Daniel Lezcano To: rafael@kernel.org Cc: linux-pm@vger.kernel.org, shuah@kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, manaf.pallikunhi@oss.qualcomm.com Subject: [PATCH 2/2] selftests/powercap: Add powercap hierarchy creation API tests Date: Thu, 6 Aug 2026 13:01:59 +0200 Message-ID: <20260806110159.69690-3-daniel.lezcano@oss.qualcomm.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260806110159.69690-1-daniel.lezcano@oss.qualcomm.com> References: <20260806110159.69690-1-daniel.lezcano@oss.qualcomm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-GUID: _iBsJ6h_ZXyI_z_n2n-x3DNxLUklYJ7Y X-Proofpoint-ORIG-GUID: _iBsJ6h_ZXyI_z_n2n-x3DNxLUklYJ7Y X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwODA2MDA4NyBTYWx0ZWRfX+w501o/s3IuW INbn9BQfTc2ty5CDOk8jgwB6xmMXG/O4zGa9eG67IXv693wG3m19GE65OzvpoLhvl3eHWlulc5z SXa7F/FfFrxbKGne/p7iBw62aeRIwlzom2+EpnnV6rKVtbeJTgZq0fmYHLUUNM7TJWoatR5srpu retH9fI7ARBbMoM+qcrNHFk3iC8IhMaa6rqeg7zyJFdcV9cnPFeLt+Plr77UsP+Iu4Un+VdD5Yu +LW6gg23U0caLkL9nngWU+M13caRnGL9l1VKQLvxRRF6SiZuihGr2RFGq0ZsXGXDk+xf2qhEEC3 DhikXbkZwIQE/rYJIYK+99XXyim6aeTHbOX8fOo02yfxt5FpRCLH0Lo6k3EJmRWdm2gNWXugzGM owdaLCSTGP3AueVyGDJKF0GUtECCr/xslkXU/X9P4M7KAYQS4GZDKy32fkzRFWRS9f9fenEiRYV DwqxqWYUN8k8daIeT2g== X-Proofpoint-Spam-Info: AW1haW4tMjYwODA2MDA4NyBTYWx0ZWRfX72e56gHR8zGg dBx09PpZhNwBAlLi+zsJfTgJZVicFoKFF13wmiCaHnzzHogInWR9M5tRrRGSZU/2WMnzUZAxyfX pgaHBCkP8nM86D/oexnFbusSYlOVTAk= X-Authority-Analysis: v=2.4 cv=bdtbluPB c=1 sm=1 tr=0 ts=6a7469bb cx=c_pps a=72HoHk1woDtn7btP4rdmlg==:117 a=xqWC_Br6kY4A:10 a=Sv0fKeRqtYgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=YMgV9FUhrdKAYTUUvYB2:22 a=EUspDBNiAAAA:8 a=xFgmAgSJGoImdcxQjNwA:9 a=kA6IBgd4cpdPkAWqgNAz:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-08-05_06,2026-08-05_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 phishscore=0 clxscore=1015 spamscore=0 bulkscore=0 malwarescore=0 suspectscore=0 lowpriorityscore=0 priorityscore=1501 adultscore=0 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2608060087 Content-Type: text/plain; charset="utf-8" The powercap hierarchy API introduces generic helpers to duplicate, instantiate and destroy a complete powercap hierarchy from a static description. Add a kselftest exercising this API. The test builds a synthetic powercap hierarchy composed of a package, three CPU clusters and twelve CPUs, registers it as a powercap control type and verifies that the expected sysfs hierarchy and attributes are created. It also validates that the hierarchy is correctly removed when the module is unloaded. The test consists of: * a kernel module implementing a synthetic powercap hierarchy and dummy callbacks; * a userspace kselftest script that loads the module, validates the exported sysfs hierarchy and attribute values, unloads the module and verifies that all objects have been removed. This provides a regression test for the powercap hierarchy helpers and their integration with the powercap core. Cc: Manaf Meethalavalappu Pallikunhi Signed-off-by: Daniel Lezcano Tested-by: Manaf Meethalavalappu Pallikunhi=20 --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/powercap/Kbuild | 3 + tools/testing/selftests/powercap/Makefile | 17 ++ .../selftests/powercap/powercap_hierarchy.c | 247 ++++++++++++++++++ .../selftests/powercap/powercap_hierarchy.sh | 118 +++++++++ 5 files changed, 386 insertions(+) create mode 100644 tools/testing/selftests/powercap/Kbuild create mode 100644 tools/testing/selftests/powercap/Makefile create mode 100644 tools/testing/selftests/powercap/powercap_hierarchy.c create mode 100755 tools/testing/selftests/powercap/powercap_hierarchy.sh diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Mak= efile index 8d4db2241cc2..2fac671e9145 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -93,6 +93,7 @@ TARGETS +=3D pidfd TARGETS +=3D pid_namespace TARGETS +=3D pipe TARGETS +=3D power_supply +TARGETS +=3D powercap TARGETS +=3D powerpc TARGETS +=3D prctl TARGETS +=3D proc diff --git a/tools/testing/selftests/powercap/Kbuild b/tools/testing/selfte= sts/powercap/Kbuild new file mode 100644 index 000000000000..baa0d0ba9593 --- /dev/null +++ b/tools/testing/selftests/powercap/Kbuild @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-m +=3D powercap_hierarchy.o \ No newline at end of file diff --git a/tools/testing/selftests/powercap/Makefile b/tools/testing/self= tests/powercap/Makefile new file mode 100644 index 000000000000..210d6e6c5577 --- /dev/null +++ b/tools/testing/selftests/powercap/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 + +# User-space test script +TEST_PROGS :=3D powercap_hierarchy.sh + +# Kernel module built as part of the test +TEST_FILES :=3D powercap_hierarchy.ko + +KDIR ?=3D $(if $(O),$(O),$(realpath ../../../..)) + +all: + $(MAKE) -C $(KDIR) M=3D$(CURDIR) modules + +clean: + $(MAKE) -C $(KDIR) M=3D$(CURDIR) clean + +include ../lib.mk diff --git a/tools/testing/selftests/powercap/powercap_hierarchy.c b/tools/= testing/selftests/powercap/powercap_hierarchy.c new file mode 100644 index 000000000000..9531fc86aa01 --- /dev/null +++ b/tools/testing/selftests/powercap/powercap_hierarchy.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Author: Daniel Lezcano + * + * Powercap hierarchy description test module + */ +#include + +struct pch_test_data { + int value; +}; + +static struct powercap_node __initdata pch_test_nodes[] =3D { + [0] =3D { .name =3D "package", + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [1] =3D { .name =3D "cluster0", .parent =3D &pch_test_nodes[0], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [2] =3D { .name =3D "cluster1", .parent =3D &pch_test_nodes[0], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [3] =3D { .name =3D "cluster2", .parent =3D &pch_test_nodes[0], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [4] =3D { .name =3D "cpu0", .parent =3D &pch_test_nodes[1], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [5] =3D { .name =3D "cpu1", .parent =3D &pch_test_nodes[1], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [6] =3D { .name =3D "cpu2", .parent =3D &pch_test_nodes[1], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [7] =3D { .name =3D "cpu3", .parent =3D &pch_test_nodes[1], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [8] =3D { .name =3D "cpu4", .parent =3D &pch_test_nodes[2], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [9] =3D { .name =3D "cpu5", .parent =3D &pch_test_nodes[2], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [10] =3D { .name =3D "cpu6", .parent =3D &pch_test_nodes[2], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [11] =3D { .name =3D "cpu7", .parent =3D &pch_test_nodes[2], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [12] =3D { .name =3D "cpu8", .parent =3D &pch_test_nodes[3], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [13] =3D { .name =3D "cpu9", .parent =3D &pch_test_nodes[3], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [14] =3D { .name =3D "cpu10", .parent =3D &pch_test_nodes[3], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, + [15] =3D { .name =3D "cpu11", .parent =3D &pch_test_nodes[3], + .data =3D &(struct pch_test_data) { .value =3D 0xDEADBEEF } }, +}; + +static struct powercap_hierarchy __initdata pch_test_hierarchy =3D { + .nodes =3D pch_test_nodes, + .nr_nodes =3D ARRAY_SIZE(pch_test_nodes), +}; + +static struct powercap_hierarchy *hierarchy; + +static struct powercap_control_type *pct; + +struct pch_test { + struct powercap_zone zone; +}; + +static struct pch_test *to_pch_test(struct powercap_zone *pcz) +{ + return container_of(pcz, struct pch_test, zone); +} + +static int pch_test_get_max_power_range_uw(struct powercap_zone *pcz, + u64 *power_uw) +{ + *power_uw =3D 0xBADC0FFEE; + return 0; +} + +static int pch_test_get_power_uw(struct powercap_zone *pcz, + u64 *power_uw) +{ + *power_uw =3D 0xC0FFEE; + return 0; +} + +static int pch_test_release(struct powercap_zone *pcz) +{ + kfree(to_pch_test(pcz)); + return 0; +} + +static const struct powercap_zone_ops pch_test_ops =3D { + .get_max_power_range_uw =3D pch_test_get_max_power_range_uw, + .get_power_uw =3D pch_test_get_power_uw, + .release =3D pch_test_release, +}; + +static int pch_test_set_power_limit_uw(struct powercap_zone *pcz, + int cid, u64 power_uw) +{ + return 0; +} + +static int pch_test_get_power_limit_uw(struct powercap_zone *pcz, + int cid, u64 *power_uw) +{ + *power_uw =3D 0xDEADC0DE; + return 0; +} + +static int pch_test_set_time_window_us(struct powercap_zone *pcz, + int cid, u64 power_uw) +{ + return 0; +} + +static int pch_test_get_time_window_us(struct powercap_zone *pcz, + int cid, u64 *power_uw) +{ + *power_uw =3D 0xDEADC0DE; + return 0; +} + +static int pch_test_get_max_power_uw(struct powercap_zone *pcz, + int cid, u64 *power_uw) +{ + *power_uw =3D 0xDEADC0DE; + return 0; +} + +static const char *pch_test_get_name(struct powercap_zone *pcz, int cid) +{ + return "my constraint name"; +} + +static const struct powercap_zone_constraint_ops pch_test_constraint_ops = =3D { + .set_power_limit_uw =3D pch_test_set_power_limit_uw, + .get_power_limit_uw =3D pch_test_get_power_limit_uw, + .set_time_window_us =3D pch_test_set_time_window_us, + .get_time_window_us =3D pch_test_get_time_window_us, + .get_max_power_uw =3D pch_test_get_max_power_uw, + .get_name =3D pch_test_get_name, +}; + +static struct powercap_zone *pch_test_create(struct powercap_control_type = *pct, + const char *name, void *data, + struct powercap_zone *parent) +{ + struct pch_test_data *pcht_data =3D data; + struct pch_test *pcht; + struct powercap_zone *pcz; + + if (!pct) { + pr_err("Invalid NULL controller type\n"); + return ERR_PTR(-EINVAL); + } + + if (!name) { + pr_err("Invalid NULL name\n"); + return ERR_PTR(-EINVAL); + } + + if (pcht_data->value !=3D 0xDEADBEEF) { + pr_err("Invalid pcht data !=3D 0xDEADBEEF"); + return ERR_PTR(-EINVAL); + } + + pcht =3D kzalloc_obj(*pcht); + if (!pcht) + return ERR_PTR(-ENOMEM); + + pcz =3D powercap_register_zone(&pcht->zone, pct, name, parent, + &pch_test_ops, 1, &pch_test_constraint_ops); + if (IS_ERR(pcz)) { + pr_err("Failed to register powercap zone '%s': %ld\n", + name, PTR_ERR(pcz)); + } + + return pcz; +} + +static void pch_test_destroy(struct powercap_control_type *pct, + struct powercap_zone *zone, + void *data) +{ + struct pch_test_data *pcht_data =3D data; + + if (!pct) { + pr_err("Invalid NULL controller type\n"); + return; + } + + if (!zone) { + pr_err("Invalid NULL zone\n"); + return; + } + + if (pcht_data->value !=3D 0xDEADBEEF) { + pr_err("Invalid pcht data !=3D 0xDEADBEEF"); + return; + } + + powercap_unregister_zone(pct, zone); +} + +static int __init pch_test_init(void) +{ + int ret; + + hierarchy =3D powercap_hierarchy_dup(&pch_test_hierarchy); + if (IS_ERR(hierarchy)) { + ret =3D PTR_ERR(hierarchy); + pr_err("Failed to dup the hierarchy: %d\n", ret); + return ret; + } + + pct =3D powercap_register_control_type(NULL, "powercap-test", NULL); + if (IS_ERR(pct)) { + ret =3D PTR_ERR(pct); + pr_err("Failed to register control type: %d\n", ret); + goto out_free_hierarchy; + } + + ret =3D powercap_hierarchy_create(pct, hierarchy, pch_test_create, pch_te= st_destroy); + if (ret) { + pr_err("Failed to create the hierarchy: %d\n", ret); + goto out_unregister_pct; + } + + return 0; + +out_unregister_pct: + powercap_unregister_control_type(pct); +out_free_hierarchy: + powercap_hierarchy_free(hierarchy); + return ret; +} +module_init(pch_test_init); + +static void __exit pch_test_exit(void) +{ + powercap_hierarchy_destroy(pct, hierarchy, pch_test_destroy); + powercap_hierarchy_free(hierarchy); + powercap_unregister_control_type(pct); +} +module_exit(pch_test_exit); + +MODULE_DESCRIPTION("Powercap hierarchy test driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Daniel Lezcano