From nobody Sat Jul 25 01:02:59 2026 Received: from mailgw.kylinos.cn (mailgw.kylinos.cn [124.126.103.232]) (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 E743930566C; Tue, 21 Jul 2026 09:25:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=124.126.103.232 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784625950; cv=none; b=QmEzNr5/NmjfyXEVqz1r+zpv1rjTxfZUMCUrTuN9w5i/LUG5wR0Kc582mkoHKTBI74eNfIQsqxWo2BroS2SuS4hCEvjFfp5OHdKTHhOqLemrCoQK8+dWlIOGcDmqDDEWKlLTmd4fVzdWklYrRaWyF1n0oGeTfg2raFiQdusy2qI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784625950; c=relaxed/simple; bh=D4MIUHXlhkumx8h96KZ7RU4b3HNTX3DDdQy4oHrg9/E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bNBY3N7IFSveGGIPkVZ5rQ8cfa0CfnA49I4WOmvVKaYMm4IR6Wy0FR7LaU14DqTuXwQqp9VchTeAwebtQfZyq+953CEnDSgoiBCKtYZs2ZcDby+i5/+Z49RzqqiZI+/VJ17qbvI1aoAjCjjA66jbiRsidBCk0vIwmAv1ws2FD6k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn; spf=pass smtp.mailfrom=kylinos.cn; arc=none smtp.client-ip=124.126.103.232 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kylinos.cn X-UUID: 229ac3bc84e611f1aa26b74ffac11d73-20260721 X-CTIC-Tags: HR_CC_COUNT, HR_CC_DOMAIN_COUNT, HR_CC_NAME, HR_CC_NO_NAME, HR_CTE_8B HR_CTT_MISS, HR_DATE_H, HR_DATE_WKD, HR_DATE_ZONE, HR_FROM_NAME HR_SJ_DIGIT_LEN, HR_SJ_LANG, HR_SJ_LEN, HR_SJ_LETTER, HR_SJ_NOR_SYM HR_SJ_PHRASE, HR_SJ_PHRASE_LEN, HR_SJ_WS, HR_TO_COUNT, HR_TO_DOMAIN_COUNT HR_TO_NO_NAME, IP_UNTRUSTED, SRC_UNTRUSTED, IP_UNFAMILIAR, SRC_UNFAMILIAR DN_TRUSTED, SRC_TRUSTED, SA_TRUSTED, SA_EXISTED, SN_TRUSTED SN_EXISTED, SPF_NOPASS, DKIM_NOPASS, DMARC_NOPASS, CIE_GOOD_SPF CIE_UNKNOWN, GTI_FG_BS, GTI_RG_INFO, GTI_C_BU, AMN_GOOD ABX_MISS_RDNS X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:df4f7e0b-2fd5-4800-8ab2-ca99319e9ae4,IP:10, URL:0,TC:0,Content:0,EDM:25,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTIO N:release,TS:35 X-CID-INFO: VERSION:1.3.12,REQID:df4f7e0b-2fd5-4800-8ab2-ca99319e9ae4,IP:10,UR L:0,TC:0,Content:0,EDM:25,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION: release,TS:35 X-CID-META: VersionHash:e7bac3a,CLOUDID:f872efece75d71c7aa5bc6ee5e263e50,BulkI D:260721172540CARXOK64,BulkQuantity:0,Recheck:0,SF:17|19|38|66|78|81|82|10 2|127|865|898,TC:nil,Content:0|15|50,EDM:5,IP:-2,URL:0,File:nil,RT:nil,Bul k:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0 ,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR,TF_CID_SPAM_FAS,TF_CID_SPAM_FSD X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: 229ac3bc84e611f1aa26b74ffac11d73-20260721 X-User: tianyaxiong@kylinos.cn Received: from tian-x86.. [(175.2.17.192)] by mailgw.kylinos.cn (envelope-from ) (Generic MTA) with ESMTP id 1050882661; Tue, 21 Jul 2026 17:25:38 +0800 From: Yaxiong Tian To: rafael@kernel.org, daniel.lezcano@kernel.org, christian.loehle@arm.com, lenb@kernel.org, pavel@kernel.org, shuah@kernel.org Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-kselftest@vger.kernel.org, Yaxiong Tian Subject: [PATCH 1/5] pm: qos: add notifiers for CPU latency and wakeup latency QoS Date: Tue, 21 Jul 2026 17:25:34 +0800 Message-ID: <20260721092534.18718-1-tianyaxiong@kylinos.cn> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721092324.17508-1-tianyaxiong@kylinos.cn> References: <20260721092324.17508-1-tianyaxiong@kylinos.cn> 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 Content-Type: text/plain; charset="utf-8" Expose notifier registration for the system-wide CPU latency and wakeup latency QoS constraints so listeners can react when the aggregate target value changes. pm_qos_update_target() already invokes constraint notifiers when the effective value changes. Signed-off-by: Yaxiong Tian --- include/linux/pm_qos.h | 23 +++++++++++++++++++ kernel/power/qos.c | 50 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 6cea4455f867..3759b453c6d6 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -149,6 +149,8 @@ bool cpu_latency_qos_request_active(struct pm_qos_reque= st *req); void cpu_latency_qos_add_request(struct pm_qos_request *req, s32 value); void cpu_latency_qos_update_request(struct pm_qos_request *req, s32 new_va= lue); void cpu_latency_qos_remove_request(struct pm_qos_request *req); +int cpu_latency_qos_add_notifier(struct notifier_block *notifier); +int cpu_latency_qos_remove_notifier(struct notifier_block *notifier); #else static inline s32 cpu_latency_qos_limit(void) { return INT_MAX; } static inline bool cpu_latency_qos_request_active(struct pm_qos_request *r= eq) @@ -160,15 +162,36 @@ static inline void cpu_latency_qos_add_request(struct= pm_qos_request *req, static inline void cpu_latency_qos_update_request(struct pm_qos_request *r= eq, s32 new_value) {} static inline void cpu_latency_qos_remove_request(struct pm_qos_request *r= eq) {} +static inline int cpu_latency_qos_add_notifier(struct notifier_block *noti= fier) +{ + return 0; +} +static inline int +cpu_latency_qos_remove_notifier(struct notifier_block *notifier) +{ + return 0; +} #endif =20 #ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP s32 cpu_wakeup_latency_qos_limit(void); +int cpu_wakeup_latency_qos_add_notifier(struct notifier_block *notifier); +int cpu_wakeup_latency_qos_remove_notifier(struct notifier_block *notifier= ); #else static inline s32 cpu_wakeup_latency_qos_limit(void) { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; } +static inline int +cpu_wakeup_latency_qos_add_notifier(struct notifier_block *notifier) +{ + return 0; +} +static inline int +cpu_wakeup_latency_qos_remove_notifier(struct notifier_block *notifier) +{ + return 0; +} #endif =20 #ifdef CONFIG_PM diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 1944dbeb0d4c..392ab3dbb923 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -212,12 +212,15 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, #ifdef CONFIG_CPU_IDLE /* Definitions related to the CPU latency QoS. */ =20 +static BLOCKING_NOTIFIER_HEAD(cpu_latency_qos_notifiers); + static struct pm_qos_constraints cpu_latency_constraints =3D { .list =3D PLIST_HEAD_INIT(cpu_latency_constraints.list), .target_value =3D PM_QOS_CPU_LATENCY_DEFAULT_VALUE, .default_value =3D PM_QOS_CPU_LATENCY_DEFAULT_VALUE, .no_constraint_value =3D PM_QOS_CPU_LATENCY_DEFAULT_VALUE, .type =3D PM_QOS_MIN, + .notifiers =3D &cpu_latency_qos_notifiers, }; =20 static inline bool cpu_latency_qos_value_invalid(s32 value) @@ -335,6 +338,28 @@ void cpu_latency_qos_remove_request(struct pm_qos_requ= est *req) } EXPORT_SYMBOL_GPL(cpu_latency_qos_remove_request); =20 +/** + * cpu_latency_qos_add_notifier - Add CPU latency QoS change notifier. + * @notifier: Notifier block managed by the caller. + */ +int cpu_latency_qos_add_notifier(struct notifier_block *notifier) +{ + return blocking_notifier_chain_register(&cpu_latency_qos_notifiers, + notifier); +} +EXPORT_SYMBOL_GPL(cpu_latency_qos_add_notifier); + +/** + * cpu_latency_qos_remove_notifier - Remove CPU latency QoS change notifie= r. + * @notifier: Notifier block previously registered. + */ +int cpu_latency_qos_remove_notifier(struct notifier_block *notifier) +{ + return blocking_notifier_chain_unregister(&cpu_latency_qos_notifiers, + notifier); +} +EXPORT_SYMBOL_GPL(cpu_latency_qos_remove_notifier); + /* User space interface to the CPU latency QoS via misc device. */ =20 static int cpu_latency_qos_open(struct inode *inode, struct file *filp) @@ -417,12 +442,15 @@ static struct miscdevice cpu_latency_qos_miscdev =3D { =20 #ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP /* The CPU system wakeup latency QoS. */ +static BLOCKING_NOTIFIER_HEAD(cpu_wakeup_latency_qos_notifiers); + static struct pm_qos_constraints cpu_wakeup_latency_constraints =3D { .list =3D PLIST_HEAD_INIT(cpu_wakeup_latency_constraints.list), .target_value =3D PM_QOS_RESUME_LATENCY_NO_CONSTRAINT, .default_value =3D PM_QOS_RESUME_LATENCY_NO_CONSTRAINT, .no_constraint_value =3D PM_QOS_RESUME_LATENCY_NO_CONSTRAINT, .type =3D PM_QOS_MIN, + .notifiers =3D &cpu_wakeup_latency_qos_notifiers, }; =20 /** @@ -436,6 +464,28 @@ s32 cpu_wakeup_latency_qos_limit(void) return pm_qos_read_value(&cpu_wakeup_latency_constraints); } =20 +/** + * cpu_wakeup_latency_qos_add_notifier - Add wakeup latency QoS notifier. + * @notifier: Notifier block managed by the caller. + */ +int cpu_wakeup_latency_qos_add_notifier(struct notifier_block *notifier) +{ + return blocking_notifier_chain_register(&cpu_wakeup_latency_qos_notifiers, + notifier); +} +EXPORT_SYMBOL_GPL(cpu_wakeup_latency_qos_add_notifier); + +/** + * cpu_wakeup_latency_qos_remove_notifier - Remove wakeup latency QoS noti= fier. + * @notifier: Notifier block previously registered. + */ +int cpu_wakeup_latency_qos_remove_notifier(struct notifier_block *notifier) +{ + return blocking_notifier_chain_unregister(&cpu_wakeup_latency_qos_notifie= rs, + notifier); +} +EXPORT_SYMBOL_GPL(cpu_wakeup_latency_qos_remove_notifier); + static int cpu_wakeup_latency_qos_open(struct inode *inode, struct file *f= ilp) { struct pm_qos_request *req; --=20 2.43.0 From nobody Sat Jul 25 01:02:59 2026 Received: from mailgw.kylinos.cn (mailgw.kylinos.cn [124.126.103.232]) (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 292B042D750; Tue, 21 Jul 2026 09:25:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=124.126.103.232 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784625958; cv=none; b=IcGUvuFtioJoTmci+XMgpE/N2fLa41B5Cy2Ee05yqmH5JdNCkg6T8buItaaddfOwBnrLuvfeZBP2JGRrgMtanqH09be5ozW6HfA72KdFI1tsKOffQBCvDeeuLdZXRXI0GS5joAxr4Apg6diBm0JBi8uMfnSZpY1pKksTEspDUCk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784625958; c=relaxed/simple; bh=kJJjg4HGyYeu3gNC8MTBbPuChpFiVjqZ8Wcvo6sfNsI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QBdSfonBv6VRXLgwNdwCRV/oMGikZswmSEU2uDtcastRashkQbwbrt6nIJYLk7nT1kM5VhSCo8lHuhSAcbX8CeiXLabp5IAqcagl/I7U6koSwxe+XYRGiGSCHlIgyHS6cz7iGpSGqhjx2Tn4ffila5OCPipNieo7tC3m/tby6/Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn; spf=pass smtp.mailfrom=kylinos.cn; arc=none smtp.client-ip=124.126.103.232 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kylinos.cn X-UUID: 27847b7a84e611f1aa26b74ffac11d73-20260721 X-CTIC-Tags: HR_CC_COUNT, HR_CC_DOMAIN_COUNT, HR_CC_NAME, HR_CC_NO_NAME, HR_CTE_8B HR_CTT_MISS, HR_DATE_H, HR_DATE_WKD, HR_DATE_ZONE, HR_FROM_NAME HR_SJ_DIGIT_LEN, HR_SJ_LANG, HR_SJ_LEN, HR_SJ_LETTER, HR_SJ_NOR_SYM HR_SJ_PHRASE, HR_SJ_PHRASE_LEN, HR_SJ_WS, HR_TO_COUNT, HR_TO_DOMAIN_COUNT HR_TO_NO_NAME, IP_UNTRUSTED, SRC_UNTRUSTED, IP_UNFAMILIAR, SRC_UNFAMILIAR DN_TRUSTED, SRC_TRUSTED, SA_TRUSTED, SA_EXISTED, SN_TRUSTED SN_EXISTED, SPF_NOPASS, DKIM_NOPASS, DMARC_NOPASS, CIE_GOOD_SPF CIE_UNKNOWN, GTI_FG_BS, GTI_RG_INFO, GTI_C_BU, AMN_GOOD ABX_MISS_RDNS X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:a180ae83-c957-4b23-bcd1-1c0a00492c25,IP:10, URL:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTI ON:release,TS:-15 X-CID-INFO: VERSION:1.3.12,REQID:a180ae83-c957-4b23-bcd1-1c0a00492c25,IP:10,UR L:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION :release,TS:-15 X-CID-META: VersionHash:e7bac3a,CLOUDID:c57ef94c313522ea6f4f69231031d214,BulkI D:2607211725493N8X2PR8,BulkQuantity:0,Recheck:0,SF:17|19|38|66|78|81|82|10 2|127|865|898,TC:nil,Content:0|15|50,EDM:-3,IP:-2,URL:0,File:nil,RT:nil,Bu lk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR: 0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR,TF_CID_SPAM_FAS,TF_CID_SPAM_FSD X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: 27847b7a84e611f1aa26b74ffac11d73-20260721 X-User: tianyaxiong@kylinos.cn Received: from tian-x86.. [(175.2.17.192)] by mailgw.kylinos.cn (envelope-from ) (Generic MTA) with ESMTP id 687754928; Tue, 21 Jul 2026 17:25:46 +0800 From: Yaxiong Tian To: rafael@kernel.org, daniel.lezcano@kernel.org, christian.loehle@arm.com, lenb@kernel.org, pavel@kernel.org, shuah@kernel.org Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-kselftest@vger.kernel.org, Yaxiong Tian Subject: [PATCH 2/5] cpuidle: subscribe to global latency QoS notifiers Date: Tue, 21 Jul 2026 17:25:42 +0800 Message-ID: <20260721092542.18788-1-tianyaxiong@kylinos.cn> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721092324.17508-1-tianyaxiong@kylinos.cn> References: <20260721092324.17508-1-tianyaxiong@kylinos.cn> 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 Content-Type: text/plain; charset="utf-8" Register for CPU latency and wakeup latency QoS notifications and bump a per-CPU generation counter when either aggregate constraint changes. This prepares for caching the governor latency requirement without introducing a direct qos-to-cpuidle call dependency. Signed-off-by: Yaxiong Tian --- drivers/cpuidle/governor.c | 58 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c index 5d0e7f78c6c5..bc4a70d30c34 100644 --- a/drivers/cpuidle/governor.c +++ b/drivers/cpuidle/governor.c @@ -8,10 +8,13 @@ * This code is licenced under the GPL. */ =20 +#include #include #include +#include #include #include +#include #include =20 #include "cpuidle.h" @@ -22,6 +25,61 @@ LIST_HEAD(cpuidle_governors); struct cpuidle_governor *cpuidle_curr_governor; struct cpuidle_governor *cpuidle_prev_governor; =20 +/* + * Per-CPU generation bumped to invalidate that CPU's cached latency + * constraint. Consumers of the generation are added in later changes. + */ +static DEFINE_PER_CPU(atomic_t, latency_req_gen); + +static void cpuidle_latency_req_invalidate_cpu(unsigned int cpu) +{ + atomic_inc(per_cpu_ptr(&latency_req_gen, cpu)); +} + +static void cpuidle_latency_req_invalidate_all(void) +{ + unsigned int cpu; + + for_each_possible_cpu(cpu) + cpuidle_latency_req_invalidate_cpu(cpu); +} + +static int cpuidle_global_qos_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + cpuidle_latency_req_invalidate_all(); + return NOTIFY_OK; +} + +static struct notifier_block cpuidle_latency_qos_nb =3D { + .notifier_call =3D cpuidle_global_qos_notify, +}; + +#ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP +static struct notifier_block cpuidle_wakeup_qos_nb =3D { + .notifier_call =3D cpuidle_global_qos_notify, +}; +#endif + +static int __init cpuidle_latency_req_init(void) +{ + int ret; + + ret =3D cpu_latency_qos_add_notifier(&cpuidle_latency_qos_nb); + if (ret) + return ret; + +#ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP + ret =3D cpu_wakeup_latency_qos_add_notifier(&cpuidle_wakeup_qos_nb); + if (ret) { + cpu_latency_qos_remove_notifier(&cpuidle_latency_qos_nb); + return ret; + } +#endif + return 0; +} +core_initcall(cpuidle_latency_req_init); + /** * cpuidle_find_governor - finds a governor of the specified name * @str: the name --=20 2.43.0 From nobody Sat Jul 25 01:02:59 2026 Received: from mailgw.kylinos.cn (mailgw.kylinos.cn [124.126.103.232]) (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 07771437862; Tue, 21 Jul 2026 09:25:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=124.126.103.232 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784625961; cv=none; b=oz/RyKhrtXP8XqYOikFfVPK5R/aFYAMhr1rKiwfQUcJ3tHNnpSFHrC8veW1VEDidt6UYdC617cGocCUCFvnVWIj9gqE/Fmo9BLLm0XPNAhTWv4NuKmBcfpDRlRa9ZHcRFMpCZ9+80bEQlMIqXQvaBFMIGHIJmsfO2qwqHvV9vxw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784625961; c=relaxed/simple; bh=KOuIZvCRsLiQEvlpnaZ7j0M7DCtdzRxTBu0j76ihEXo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mChk0LfHtcYCvBagyMdw9My0eM1ovKwkiskGl/XNJa1wXq8WyAKqt37qA8Mt66uSPYqFiI2een7q9sN286bMmgy0r5Fi7ocI6RvaLwrGMH1Gb/E6njWmCu92C9Iec8+64GbOaW9p/HBIaEaldgDSPJ1Iw8bv7P58F5wi/Fqwmnw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn; spf=pass smtp.mailfrom=kylinos.cn; arc=none smtp.client-ip=124.126.103.232 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kylinos.cn X-UUID: 2cb872b884e611f1aa26b74ffac11d73-20260721 X-CTIC-Tags: HR_CC_COUNT, HR_CC_DOMAIN_COUNT, HR_CC_NAME, HR_CC_NO_NAME, HR_CTE_8B HR_CTT_MISS, HR_DATE_H, HR_DATE_WKD, HR_DATE_ZONE, HR_FROM_NAME HR_SJ_DIGIT_LEN, HR_SJ_LANG, HR_SJ_LEN, HR_SJ_LETTER, HR_SJ_NOR_SYM HR_SJ_PHRASE, HR_SJ_PHRASE_LEN, HR_SJ_WS, HR_TO_COUNT, HR_TO_DOMAIN_COUNT HR_TO_NO_NAME, IP_UNTRUSTED, SRC_UNTRUSTED, IP_UNFAMILIAR, SRC_UNFAMILIAR DN_TRUSTED, SRC_TRUSTED, SA_TRUSTED, SA_EXISTED, SN_TRUSTED SN_EXISTED, SPF_NOPASS, DKIM_NOPASS, DMARC_NOPASS, CIE_GOOD_SPF CIE_UNKNOWN, GTI_FG_BS, GTI_RG_INFO, GTI_C_BU, AMN_GOOD ABX_MISS_RDNS X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:227beb96-4098-469f-b2ed-d34e5d372eee,IP:10, URL:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTI ON:release,TS:-15 X-CID-INFO: VERSION:1.3.12,REQID:227beb96-4098-469f-b2ed-d34e5d372eee,IP:10,UR L:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION :release,TS:-15 X-CID-META: VersionHash:e7bac3a,CLOUDID:538dda4986c1585c841d851e35394f70,BulkI D:260721172555J9AWZBTD,BulkQuantity:0,Recheck:0,SF:17|19|38|66|78|81|82|10 2|127|865|898,TC:nil,Content:0|15|50,EDM:-3,IP:-2,URL:0,File:nil,RT:nil,Bu lk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR: 0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR,TF_CID_SPAM_FAS,TF_CID_SPAM_FSD X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: 2cb872b884e611f1aa26b74ffac11d73-20260721 X-User: tianyaxiong@kylinos.cn Received: from tian-x86.. [(175.2.17.192)] by mailgw.kylinos.cn (envelope-from ) (Generic MTA) with ESMTP id 717583555; Tue, 21 Jul 2026 17:25:55 +0800 From: Yaxiong Tian To: rafael@kernel.org, daniel.lezcano@kernel.org, christian.loehle@arm.com, lenb@kernel.org, pavel@kernel.org, shuah@kernel.org Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-kselftest@vger.kernel.org, Yaxiong Tian Subject: [PATCH 3/5] cpuidle: invalidate latency gen on per-CPU resume QoS changes Date: Tue, 21 Jul 2026 17:25:51 +0800 Message-ID: <20260721092551.18870-1-tianyaxiong@kylinos.cn> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721092324.17508-1-tianyaxiong@kylinos.cn> References: <20260721092324.17508-1-tianyaxiong@kylinos.cn> 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 Content-Type: text/plain; charset="utf-8" Register a DEV_PM_QOS_RESUME_LATENCY notifier for each CPU device and bump only that CPU's latency_req generation when its resume latency constraint changes. Signed-off-by: Yaxiong Tian --- drivers/cpuidle/cpuidle.c | 15 +++++++++--- drivers/cpuidle/cpuidle.h | 2 ++ drivers/cpuidle/governor.c | 48 +++++++++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 2d2f40a2cb81..9495f904fe85 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -613,6 +613,8 @@ static void __cpuidle_unregister_device(struct cpuidle_= device *dev) { struct cpuidle_driver *drv =3D cpuidle_get_cpu_driver(dev); =20 + cpuidle_latency_req_notifier_unregister(dev->cpu); + list_del(&dev->device_list); per_cpu(cpuidle_devices, dev->cpu) =3D NULL; module_put(drv->owner); @@ -661,10 +663,17 @@ static int __cpuidle_register_device(struct cpuidle_d= evice *dev) =20 ret =3D cpuidle_coupled_register_device(dev); if (ret) - __cpuidle_unregister_device(dev); - else - dev->registered =3D 1; + goto unreg; + + ret =3D cpuidle_latency_req_notifier_register(cpu); + if (ret) + goto unreg; =20 + dev->registered =3D 1; + return 0; + +unreg: + __cpuidle_unregister_device(dev); return ret; } =20 diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h index 52701d9588f1..151fa9ebe483 100644 --- a/drivers/cpuidle/cpuidle.h +++ b/drivers/cpuidle/cpuidle.h @@ -25,6 +25,8 @@ extern void cpuidle_uninstall_idle_handler(void); /* governors */ extern struct cpuidle_governor *cpuidle_find_governor(const char *str); extern int cpuidle_switch_governor(struct cpuidle_governor *gov); +int cpuidle_latency_req_notifier_register(unsigned int cpu); +void cpuidle_latency_req_notifier_unregister(unsigned int cpu); =20 /* sysfs */ =20 diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c index bc4a70d30c34..d286ccf19a69 100644 --- a/drivers/cpuidle/governor.c +++ b/drivers/cpuidle/governor.c @@ -27,10 +27,18 @@ struct cpuidle_governor *cpuidle_prev_governor; =20 /* * Per-CPU generation bumped to invalidate that CPU's cached latency - * constraint. Consumers of the generation are added in later changes. + * constraint. Global QoS changes invalidate every CPU; per-CPU resume + * latency changes invalidate only the affected CPU. */ static DEFINE_PER_CPU(atomic_t, latency_req_gen); =20 +struct cpuidle_cpu_qos_nb { + struct notifier_block nb; + unsigned int cpu; +}; + +static DEFINE_PER_CPU(struct cpuidle_cpu_qos_nb, cpuidle_cpu_qos_nb); + static void cpuidle_latency_req_invalidate_cpu(unsigned int cpu) { atomic_inc(per_cpu_ptr(&latency_req_gen, cpu)); @@ -61,6 +69,44 @@ static struct notifier_block cpuidle_wakeup_qos_nb =3D { }; #endif =20 +static int cpuidle_cpu_qos_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct cpuidle_cpu_qos_nb *qos_nb =3D + container_of(nb, struct cpuidle_cpu_qos_nb, nb); + + cpuidle_latency_req_invalidate_cpu(qos_nb->cpu); + return NOTIFY_OK; +} + +int cpuidle_latency_req_notifier_register(unsigned int cpu) +{ + struct device *device =3D get_cpu_device(cpu); + struct cpuidle_cpu_qos_nb *qos_nb =3D + per_cpu_ptr(&cpuidle_cpu_qos_nb, cpu); + + if (!device) + return -ENODEV; + + qos_nb->cpu =3D cpu; + qos_nb->nb.notifier_call =3D cpuidle_cpu_qos_notify; + return dev_pm_qos_add_notifier(device, &qos_nb->nb, + DEV_PM_QOS_RESUME_LATENCY); +} + +void cpuidle_latency_req_notifier_unregister(unsigned int cpu) +{ + struct device *device =3D get_cpu_device(cpu); + struct cpuidle_cpu_qos_nb *qos_nb =3D + per_cpu_ptr(&cpuidle_cpu_qos_nb, cpu); + + if (!device) + return; + + dev_pm_qos_remove_notifier(device, &qos_nb->nb, + DEV_PM_QOS_RESUME_LATENCY); +} + static int __init cpuidle_latency_req_init(void) { int ret; --=20 2.43.0 From nobody Sat Jul 25 01:02:59 2026 Received: from mailgw.kylinos.cn (mailgw.kylinos.cn [124.126.103.232]) (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 9BEE54302F1; Tue, 21 Jul 2026 09:26:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=124.126.103.232 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784625971; cv=none; b=JI23FkTprbsOA1EdZ+N2IOfhQr+hwHyMtfqtMz2ChIMTl/t/k2RPHfj2J4avQ4TdhK/+huTgCr2SSdy+LMoW5E+dSGz688ZYr0Kg6t/MZOl7AbxvxCaVgil995nwCB0RJ296jCzhlicBG2qnSySsOWE9eZYwRVOQArpEus68dk0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784625971; c=relaxed/simple; bh=SYuYEQr3KC882LOXsv5y3jmDRURaR85r/Vd7elKeXU0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=p7frwz3o2QY9J7WbV5qEuBSsUxVyi5OfHgikHTtFkk9evp4CSsH0c1rkZaCH01/J0ejfnOBRDFVWXNz0rjWO6y9IR59G/489XT2ZU4guMdXzcHNGnM691nXKZyN5ipr13mly7EbfofMZ+3X8mKnCAcZxDskYwXHTR/kRkwjx9Mk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn; spf=pass smtp.mailfrom=kylinos.cn; arc=none smtp.client-ip=124.126.103.232 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kylinos.cn X-UUID: 31564f3484e611f1aa26b74ffac11d73-20260721 X-CTIC-Tags: HR_CC_COUNT, HR_CC_DOMAIN_COUNT, HR_CC_NAME, HR_CC_NO_NAME, HR_CTE_8B HR_CTT_MISS, HR_DATE_H, HR_DATE_WKD, HR_DATE_ZONE, HR_FROM_NAME HR_SJ_DIGIT_LEN, HR_SJ_LANG, HR_SJ_LEN, HR_SJ_LETTER, HR_SJ_NOR_SYM HR_SJ_PHRASE, HR_SJ_PHRASE_LEN, HR_SJ_WS, HR_TO_COUNT, HR_TO_DOMAIN_COUNT HR_TO_NO_NAME, IP_UNTRUSTED, SRC_UNTRUSTED, IP_UNFAMILIAR, SRC_UNFAMILIAR DN_TRUSTED, SRC_TRUSTED, SA_TRUSTED, SA_EXISTED, SN_TRUSTED SN_EXISTED, SPF_NOPASS, DKIM_NOPASS, DMARC_NOPASS, CIE_GOOD_SPF CIE_UNKNOWN, GTI_FG_BS, GTI_RG_INFO, GTI_C_BU, AMN_GOOD ABX_MISS_RDNS X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:df15d5d9-d5e0-4add-bb14-b34e84641233,IP:10, URL:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTI ON:release,TS:-15 X-CID-INFO: VERSION:1.3.12,REQID:df15d5d9-d5e0-4add-bb14-b34e84641233,IP:10,UR L:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION :release,TS:-15 X-CID-META: VersionHash:e7bac3a,CLOUDID:addffd65343360c0e8e7c4003aebabdf,BulkI D:260721172604V5C13W1K,BulkQuantity:0,Recheck:0,SF:17|19|38|66|78|81|82|10 2|127|865|898,TC:nil,Content:0|15|50,EDM:-3,IP:-2,URL:0,File:nil,RT:nil,Bu lk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR: 0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_FAS,TF_CID_SPAM_FSD,TF_CID_SPAM_SNR X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: 31564f3484e611f1aa26b74ffac11d73-20260721 X-User: tianyaxiong@kylinos.cn Received: from tian-x86.. [(175.2.17.192)] by mailgw.kylinos.cn (envelope-from ) (Generic MTA) with ESMTP id 314397269; Tue, 21 Jul 2026 17:26:03 +0800 From: Yaxiong Tian To: rafael@kernel.org, daniel.lezcano@kernel.org, christian.loehle@arm.com, lenb@kernel.org, pavel@kernel.org, shuah@kernel.org Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-kselftest@vger.kernel.org, Yaxiong Tian Subject: [PATCH 4/5] cpuidle: cache aggregated governor latency QoS constraint Date: Tue, 21 Jul 2026 17:25:59 +0800 Message-ID: <20260721092559.18921-1-tianyaxiong@kylinos.cn> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721092324.17508-1-tianyaxiong@kylinos.cn> References: <20260721092324.17508-1-tianyaxiong@kylinos.cn> 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 Content-Type: text/plain; charset="utf-8" cpuidle_governor_latency_req() runs on every idle-state selection and aggregates per-CPU resume latency with the global CPU latency and wakeup latency QoS limits. That path repeatedly walks get_cpu_device() and pm_qos_read_value(), which shows up hot under menu_select(). Cache the aggregated constraint per CPU and refresh it only when the corresponding per-CPU generation changes. The generation is already bumped by the global and per-CPU resume latency QoS notifiers added earlier in this series. On a menu governor profile, cpuidle_governor_latency_req() drops from about 19.9% of menu_select time to about 4.2%, roughly a 6x reduction in cost per call (~1.9 us down to ~0.3 us). Signed-off-by: Yaxiong Tian --- drivers/cpuidle/governor.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c index d286ccf19a69..67909470c14b 100644 --- a/drivers/cpuidle/governor.c +++ b/drivers/cpuidle/governor.c @@ -32,11 +32,17 @@ struct cpuidle_governor *cpuidle_prev_governor; */ static DEFINE_PER_CPU(atomic_t, latency_req_gen); =20 +struct cpuidle_latency_req_cache { + unsigned int gen; + s64 latency_ns; +}; + struct cpuidle_cpu_qos_nb { struct notifier_block nb; unsigned int cpu; }; =20 +static DEFINE_PER_CPU(struct cpuidle_latency_req_cache, latency_req_cache); static DEFINE_PER_CPU(struct cpuidle_cpu_qos_nb, cpuidle_cpu_qos_nb); =20 static void cpuidle_latency_req_invalidate_cpu(unsigned int cpu) @@ -212,10 +218,22 @@ int cpuidle_register_governor(struct cpuidle_governor= *gov) */ s64 cpuidle_governor_latency_req(unsigned int cpu) { - struct device *device =3D get_cpu_device(cpu); - int device_req =3D dev_pm_qos_raw_resume_latency(device); - int global_req =3D cpu_latency_qos_limit(); - int global_wake_req =3D cpu_wakeup_latency_qos_limit(); + struct cpuidle_latency_req_cache *cache; + unsigned int gen; + struct device *device; + int device_req, global_req, global_wake_req; + s64 latency_ns; + + cache =3D per_cpu_ptr(&latency_req_cache, cpu); + gen =3D atomic_read(per_cpu_ptr(&latency_req_gen, cpu)); + + if (likely(READ_ONCE(cache->gen) =3D=3D gen)) + return READ_ONCE(cache->latency_ns); + + device =3D get_cpu_device(cpu); + device_req =3D dev_pm_qos_raw_resume_latency(device); + global_req =3D cpu_latency_qos_limit(); + global_wake_req =3D cpu_wakeup_latency_qos_limit(); =20 if (global_req > global_wake_req) global_req =3D global_wake_req; @@ -223,5 +241,14 @@ s64 cpuidle_governor_latency_req(unsigned int cpu) if (device_req > global_req) device_req =3D global_req; =20 - return (s64)device_req * NSEC_PER_USEC; + latency_ns =3D (s64)device_req * NSEC_PER_USEC; + + WRITE_ONCE(cache->latency_ns, latency_ns); + /* + * Store gen last so a concurrent invalidate cannot leave a stale + * latency_ns marked as current. + */ + WRITE_ONCE(cache->gen, gen); + + return latency_ns; } --=20 2.43.0 From nobody Sat Jul 25 01:02:59 2026 Received: from mailgw.kylinos.cn (mailgw.kylinos.cn [124.126.103.232]) (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 A234F408622; Tue, 21 Jul 2026 09:26:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=124.126.103.232 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784625982; cv=none; b=KTP/pae6DS9vmRtPgyezPW+6yPLVw1evdox8w1HnXgP2BzFAsBF8T6ZVnaKwu/qo+lOqryxXsZTxZXM66Hw4yRfgVktSOzNyDGTJOSlGG8uFs6vQyyYFEtA0zoQQTm2sgP7AdHNNjBDMIQttunH2Nnph3oBbWVqT2B5JcCRU62M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784625982; c=relaxed/simple; bh=lD+7FdOPhApv4Tw+KqYI4TNak8r9HBkXixM4+r+VAng=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=er+v9tdlvsfBZMv2ASWx4uhtpUMj6Mvn5XWzD6clwSQsesmildYnWfjVeKi9pMhqZKiUsGHr95l7bthcSJlaL0EhRA8Zw6uXSPrVysMs4QKsD3x6bjrYM3Dam5RSRP3Vm5O8pVgkcHnLLhRifC5duh7aKpQin8h5kOftUFldZ6I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn; spf=pass smtp.mailfrom=kylinos.cn; arc=none smtp.client-ip=124.126.103.232 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kylinos.cn X-UUID: 36453bd684e611f1aa26b74ffac11d73-20260721 X-CTIC-Tags: HR_CC_COUNT, HR_CC_DOMAIN_COUNT, HR_CC_NAME, HR_CC_NO_NAME, HR_CTE_8B HR_CTT_MISS, HR_DATE_H, HR_DATE_WKD, HR_DATE_ZONE, HR_FROM_NAME HR_SJ_DIGIT_LEN, HR_SJ_LANG, HR_SJ_LEN, HR_SJ_LETTER, HR_SJ_NOR_SYM HR_SJ_PHRASE, HR_SJ_PHRASE_LEN, HR_SJ_WS, HR_TO_COUNT, HR_TO_DOMAIN_COUNT HR_TO_NO_NAME, IP_UNTRUSTED, SRC_UNTRUSTED, IP_UNFAMILIAR, SRC_UNFAMILIAR DN_TRUSTED, SRC_TRUSTED, SA_TRUSTED, SA_EXISTED, SN_TRUSTED SN_EXISTED, SPF_NOPASS, DKIM_NOPASS, DMARC_NOPASS, CIE_GOOD_SPF CIE_UNKNOWN, GTI_FG_BS, GTI_RG_INFO, GTI_C_BU, AMN_GOOD ABX_MISS_RDNS X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:35331360-511e-4e5f-b4c3-503e3c2be03c,IP:10, URL:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION :release,TS:10 X-CID-INFO: VERSION:1.3.12,REQID:35331360-511e-4e5f-b4c3-503e3c2be03c,IP:10,UR L:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION:r elease,TS:10 X-CID-META: VersionHash:e7bac3a,CLOUDID:c991d7ebb74d2dae08bcf997a70de8a8,BulkI D:260721172613F1BULDZ0,BulkQuantity:0,Recheck:0,SF:17|19|38|66|78|81|82|10 2|127|865|898,TC:nil,Content:0|15|50,EDM:-3,IP:-2,URL:0,File:nil,RT:nil,Bu lk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR: 0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR,TF_CID_SPAM_FAS,TF_CID_SPAM_FSD X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: 36453bd684e611f1aa26b74ffac11d73-20260721 X-User: tianyaxiong@kylinos.cn Received: from tian-x86.. [(175.2.17.192)] by mailgw.kylinos.cn (envelope-from ) (Generic MTA) with ESMTP id 1789379040; Tue, 21 Jul 2026 17:26:11 +0800 From: Yaxiong Tian To: rafael@kernel.org, daniel.lezcano@kernel.org, christian.loehle@arm.com, lenb@kernel.org, pavel@kernel.org, shuah@kernel.org Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-kselftest@vger.kernel.org, Yaxiong Tian Subject: [PATCH 5/5] selftests/cpuidle: add latency_req QoS idle-state selection test Date: Tue, 21 Jul 2026 17:26:08 +0800 Message-ID: <20260721092608.18973-1-tianyaxiong@kylinos.cn> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721092324.17508-1-tianyaxiong@kylinos.cn> References: <20260721092324.17508-1-tianyaxiong@kylinos.cn> 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 Content-Type: text/plain; charset="utf-8" Verify that CPU latency, wakeup latency, and per-CPU resume latency QoS ceilings prevent governors from selecting idle states whose exit latency exceeds the constraint, by comparing cpuidle state usage deltas under each QoS path. USE: sudo make -C tools/testing/selftests TARGETS=3Dcpuidle run_tests Signed-off-by: Yaxiong Tian --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/cpuidle/Makefile | 6 + tools/testing/selftests/cpuidle/config | 3 + .../cpuidle/cpuidle_latency_req_qos.py | 331 ++++++++++++++++++ tools/testing/selftests/cpuidle/settings | 2 + 5 files changed, 343 insertions(+) create mode 100644 tools/testing/selftests/cpuidle/Makefile create mode 100644 tools/testing/selftests/cpuidle/config create mode 100755 tools/testing/selftests/cpuidle/cpuidle_latency_req_qos= .py create mode 100644 tools/testing/selftests/cpuidle/settings diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Mak= efile index 8189f333814c..a8fb620bebd1 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -12,6 +12,7 @@ TARGETS +=3D clone3 TARGETS +=3D connector TARGETS +=3D core TARGETS +=3D cpufreq +TARGETS +=3D cpuidle TARGETS +=3D cpu-hotplug TARGETS +=3D damon TARGETS +=3D devices/error_logs diff --git a/tools/testing/selftests/cpuidle/Makefile b/tools/testing/selft= ests/cpuidle/Makefile new file mode 100644 index 000000000000..f960d72c3a65 --- /dev/null +++ b/tools/testing/selftests/cpuidle/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +all: + +TEST_PROGS :=3D cpuidle_latency_req_qos.py + +include ../lib.mk diff --git a/tools/testing/selftests/cpuidle/config b/tools/testing/selftes= ts/cpuidle/config new file mode 100644 index 000000000000..86e8f87d4e62 --- /dev/null +++ b/tools/testing/selftests/cpuidle/config @@ -0,0 +1,3 @@ +CONFIG_CPU_IDLE=3Dy +CONFIG_CPU_IDLE_GOV_MENU=3Dy +CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP=3Dy diff --git a/tools/testing/selftests/cpuidle/cpuidle_latency_req_qos.py b/t= ools/testing/selftests/cpuidle/cpuidle_latency_req_qos.py new file mode 100755 index 000000000000..875b623da530 --- /dev/null +++ b/tools/testing/selftests/cpuidle/cpuidle_latency_req_qos.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +cpuidle: verify latency_req QoS ceilings restrict idle-state selection. + +Constrains exit latency via each of the three QoS inputs and checks that +cpuidle states whose exit latency exceeds the ceiling do not gain usage: + + 1) /dev/cpu_dma_latency + 2) /dev/cpu_wakeup_latency + 3) /sys/devices/system/cpu/cpuN/power/pm_qos_resume_latency_us +""" + +from __future__ import annotations + +import glob +import os +import struct +import sys +import time +from dataclasses import dataclass +from typing import Dict, List, Optional, Tuple + +# Source tree: tools/testing/selftests/cpuidle/../kselftest +# Install tree: kselftest_install/cpuidle/../kselftest +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), + "..", "kselftest")) +import ksft + + +CPUIDLE_BASE =3D "/sys/devices/system/cpu" +DMA_LAT_DEV =3D "/dev/cpu_dma_latency" +WAKEUP_LAT_DEV =3D "/dev/cpu_wakeup_latency" + +# Keep windows short so the whole collection fits under settings timeout. +IDLE_SEC =3D 2.0 +CPU =3D 0 + + +@dataclass +class IdleState: + index: int + name: str + latency_us: int + residency_us: int + usage: int + time_us: int + + +def read_states(cpu: int) -> Dict[int, IdleState]: + base =3D f"{CPUIDLE_BASE}/cpu{cpu}/cpuidle" + states: Dict[int, IdleState] =3D {} + paths =3D sorted( + glob.glob(f"{base}/state*"), + key=3Dlambda p: int(os.path.basename(p).replace("state", "")), + ) + for path in paths: + idx =3D int(os.path.basename(path).replace("state", "")) + states[idx] =3D IdleState( + index=3Didx, + name=3Dopen(f"{path}/name").read().strip(), + latency_us=3Dint(open(f"{path}/latency").read()), + residency_us=3Dint(open(f"{path}/residency").read()), + usage=3Dint(open(f"{path}/usage").read()), + time_us=3Dint(open(f"{path}/time").read()), + ) + return states + + +def usage_delta(before: Dict[int, IdleState], + after: Dict[int, IdleState]) -> Dict[int, int]: + return {i: after[i].usage - before[i].usage for i in before} + + +def pick_ceilings(states: Dict[int, IdleState]) -> List[int]: + nonzero =3D sorted({s.latency_us for s in states.values() if s.latency_us= > 0}) + out: List[int] =3D [] + if nonzero: + out.append(nonzero[0]) + if len(nonzero) >=3D 2: + mid =3D (nonzero[0] + nonzero[1]) // 2 + out.append(mid if mid > nonzero[0] else max(1, nonzero[1] - 1)) + if len(nonzero) >=3D 3: + out.append((nonzero[1] + nonzero[2]) // 2) + # Dedup, keep order + seen =3D set() + uniq =3D [] + for c in out: + if c not in seen and c >=3D 1: + seen.add(c) + uniq.append(c) + return uniq or [1] + + +def idle_on_cpu(seconds: float) -> None: + end =3D time.monotonic() + seconds + burn_end =3D time.monotonic() + min(0.1, seconds / 10) + while time.monotonic() < burn_end: + pass + while time.monotonic() < end: + time.sleep(0.05) + + +def allowed_states(states: Dict[int, IdleState], ceiling: int) -> List[int= ]: + return [i for i, s in states.items() if s.latency_us <=3D ceiling] + + +def forbidden_states(states: Dict[int, IdleState], ceiling: int) -> List[i= nt]: + return [i for i, s in states.items() if s.latency_us > ceiling] + + +def fmt_state_list(states: Dict[int, IdleState], idxs: List[int]) -> str: + if not idxs: + return "(none)" + return ", ".join( + f"state{i}:{states[i].name}(lat=3D{states[i].latency_us})" + for i in idxs + ) + + +def forbidden_violations(states: Dict[int, IdleState], udelta: Dict[int, i= nt], + ceiling: int) -> List[str]: + """States that must not be entered but still gained usage.""" + bad =3D [] + for i in forbidden_states(states, ceiling): + if udelta[i] > 0: + s =3D states[i] + bad.append( + f"state{i}({s.name},lat=3D{s.latency_us}) usage+=3D{udelta[i]}" + ) + return bad + + +def print_usage_table(states: Dict[int, IdleState], udelta: Dict[int, int], + ceiling: int) -> None: + ksft.print_msg( + f"{'idx':>3} {'name':<12} {'lat':>6} {'d_usage':>8} {'expect':>8}" + ) + for i in sorted(states): + s =3D states[i] + expect =3D "allow" if s.latency_us <=3D ceiling else "forbid" + ksft.print_msg( + f"{i:3d} {s.name:<12} {s.latency_us:6d} {udelta[i]:8d} {expect:>8}" + ) + + +class DmaLatencyGuard: + def __init__(self, latency_us: int): + self.latency_us =3D latency_us + self.fd =3D -1 + + def __enter__(self): + self.fd =3D os.open(DMA_LAT_DEV, os.O_RDWR) + os.write(self.fd, struct.pack("i", int(self.latency_us))) + return self + + def __exit__(self, *args): + if self.fd >=3D 0: + os.close(self.fd) + self.fd =3D -1 + + +class WakeupLatencyGuard: + def __init__(self, latency_us: int): + self.latency_us =3D latency_us + self.fd =3D -1 + + def __enter__(self): + self.fd =3D os.open(WAKEUP_LAT_DEV, os.O_RDWR) + os.write(self.fd, struct.pack("i", int(self.latency_us))) + return self + + def __exit__(self, *args): + if self.fd >=3D 0: + os.close(self.fd) + self.fd =3D -1 + + +class ResumeLatencyGuard: + def __init__(self, cpu: int, latency_us: int): + self.path =3D f"{CPUIDLE_BASE}/cpu{cpu}/power/pm_qos_resume_latency_us" + self.latency_us =3D latency_us + self.prev: Optional[str] =3D None + + def __enter__(self): + self.prev =3D open(self.path).read().strip() + with open(self.path, "w") as f: + f.write(f"{int(self.latency_us)}\n") + return self + + def __exit__(self, *args): + restore =3D "0" if self.prev in ("0", "n/a", None) else self.prev + with open(self.path, "w") as f: + f.write(f"{restore}\n") + + +def run_case(desc: str, states: Dict[int, IdleState], ceiling: int, + guard) -> None: + allow =3D allowed_states(states, ceiling) + forbid =3D forbidden_states(states, ceiling) + + ksft.print_msg(f"=3D=3D=3D {desc} =3D=3D=3D") + ksft.print_msg(f"ceiling=3D{ceiling}us") + ksft.print_msg(f"allowed: {fmt_state_list(states, allow)}") + ksft.print_msg(f"forbidden: {fmt_state_list(states, forbid)}") + + with guard: + time.sleep(0.05) + before =3D read_states(CPU) + idle_on_cpu(IDLE_SEC) + after =3D read_states(CPU) + + ud =3D usage_delta(before, after) + total =3D sum(ud.values()) + violations =3D forbidden_violations(states, ud, ceiling) + + print_usage_table(states, ud, ceiling) + ksft.print_msg( + f"total_usage+=3D{total} " + f"violations=3D{violations if violations else 'none'}" + ) + + if total <=3D 0: + ksft.test_result_fail(f"{desc}: too little idle activity ({total})") + return + if violations: + ksft.test_result_fail(f"{desc}: {'; '.join(violations)}") + return + ksft.test_result_pass(desc) + + +def build_plan(states: Dict[int, IdleState], + ceilings: List[int]) -> List[Tuple[str, int, object]]: + """Return list of (description, ceiling, context-manager factory args).""" + cases: List[Tuple[str, int, object]] =3D [] + + have_dma =3D os.path.exists(DMA_LAT_DEV) + have_wakeup =3D os.path.exists(WAKEUP_LAT_DEV) + resume_path =3D f"{CPUIDLE_BASE}/cpu{CPU}/power/pm_qos_resume_latency_us" + have_resume =3D os.path.exists(resume_path) + + for ceiling in ceilings: + if have_dma: + cases.append( + (f"cpu_dma_latency ceiling=3D{ceiling}", ceiling, + ("dma", ceiling)) + ) + else: + cases.append( + (f"cpu_dma_latency ceiling=3D{ceiling}", ceiling, + ("skip", "missing /dev/cpu_dma_latency")) + ) + + if have_wakeup: + cases.append( + (f"cpu_wakeup_latency ceiling=3D{ceiling}", ceiling, + ("wakeup", ceiling)) + ) + else: + cases.append( + (f"cpu_wakeup_latency ceiling=3D{ceiling}", ceiling, + ("skip", "missing /dev/cpu_wakeup_latency")) + ) + + if have_resume: + cases.append( + (f"pm_qos_resume_latency_us ceiling=3D{ceiling}", + ceiling, ("resume", ceiling)) + ) + else: + cases.append( + (f"pm_qos_resume_latency_us ceiling=3D{ceiling}", + ceiling, ("skip", f"missing {resume_path}")) + ) + + return cases + + +def main() -> None: + ksft.print_header() + + if os.geteuid() !=3D 0: + ksft.set_plan(1) + ksft.test_result_skip("must run as root") + ksft.finished() + + cpuidle_dir =3D f"{CPUIDLE_BASE}/cpu{CPU}/cpuidle" + if not os.path.isdir(cpuidle_dir): + ksft.set_plan(1) + ksft.test_result_skip(f"no cpuidle sysfs at {cpuidle_dir}") + ksft.finished() + + states =3D read_states(CPU) + if not states: + ksft.set_plan(1) + ksft.test_result_skip("no cpuidle states") + ksft.finished() + + gov_path =3D f"{CPUIDLE_BASE}/cpuidle/current_governor" + gov =3D open(gov_path).read().strip() if os.path.exists(gov_path) else "?" + ksft.print_msg(f"governor=3D{gov} cpu=3D{CPU}") + for i in sorted(states): + s =3D states[i] + ksft.print_msg( + f"state{i}: {s.name} latency=3D{s.latency_us}us " + f"residency=3D{s.residency_us}us" + ) + + ceilings =3D pick_ceilings(states) + ksft.print_msg(f"ceilings_us=3D{ceilings}") + cases =3D build_plan(states, ceilings) + ksft.set_plan(len(cases)) + + for desc, ceiling, kind in cases: + tag, arg =3D kind[0], kind[1] + if tag =3D=3D "skip": + ksft.test_result_skip(f"{desc}: {arg}") + continue + if tag =3D=3D "dma": + guard =3D DmaLatencyGuard(arg) + elif tag =3D=3D "wakeup": + guard =3D WakeupLatencyGuard(arg) + else: + guard =3D ResumeLatencyGuard(CPU, arg) + run_case(desc, states, ceiling, guard) + + ksft.finished() + + +if __name__ =3D=3D "__main__": + main() diff --git a/tools/testing/selftests/cpuidle/settings b/tools/testing/selft= ests/cpuidle/settings new file mode 100644 index 000000000000..5b445e716562 --- /dev/null +++ b/tools/testing/selftests/cpuidle/settings @@ -0,0 +1,2 @@ +# Multiple QoS paths x several latency ceilings x idle windows. +timeout=3D180 --=20 2.43.0