Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM: QoS: Introduce a CPU system wakeup QoS limit

Some platforms supports multiple low power states for CPUs that can be used
when entering system-wide suspend. Currently we are always selecting the
deepest possible state for the CPUs, which can break the system wakeup
latency constraint that may be required for a use case.

Let's take the first step towards addressing this problem, by introducing
an interface for user space, that allows us to specify the CPU system
wakeup QoS limit. Subsequent changes will start taking into account the new
QoS limit.

Reviewed-by: Dhruva Gole <d-gole@ti.com>
Reviewed-by: Kevin Hilman (TI) <khilman@baylibre.com>
Tested-by: Kevin Hilman (TI) <khilman@baylibre.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Link: https://patch.msgid.link/20251125112650.329269-2-ulf.hansson@linaro.org
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Ulf Hansson and committed by
Rafael J. Wysocki
a4e6512a ac3fd01e

+126
+9
include/linux/pm_qos.h
··· 162 162 static inline void cpu_latency_qos_remove_request(struct pm_qos_request *req) {} 163 163 #endif 164 164 165 + #ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP 166 + s32 cpu_wakeup_latency_qos_limit(void); 167 + #else 168 + static inline s32 cpu_wakeup_latency_qos_limit(void) 169 + { 170 + return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; 171 + } 172 + #endif 173 + 165 174 #ifdef CONFIG_PM 166 175 enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); 167 176 enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask);
+11
kernel/power/Kconfig
··· 202 202 depends on PM_WAKELOCKS 203 203 default y 204 204 205 + config PM_QOS_CPU_SYSTEM_WAKEUP 206 + bool "User space interface for CPU system wakeup QoS" 207 + depends on CPU_IDLE 208 + help 209 + Enable this to allow user space via the cpu_wakeup_latency file to 210 + specify a CPU system wakeup latency limit. 211 + 212 + This may be particularly useful for platforms supporting multiple low 213 + power states for CPUs during system-wide suspend and s2idle in 214 + particular. 215 + 205 216 config PM 206 217 bool "Device power management core functionality" 207 218 help
+106
kernel/power/qos.c
··· 415 415 .fops = &cpu_latency_qos_fops, 416 416 }; 417 417 418 + #ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP 419 + /* The CPU system wakeup latency QoS. */ 420 + static struct pm_qos_constraints cpu_wakeup_latency_constraints = { 421 + .list = PLIST_HEAD_INIT(cpu_wakeup_latency_constraints.list), 422 + .target_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT, 423 + .default_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT, 424 + .no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT, 425 + .type = PM_QOS_MIN, 426 + }; 427 + 428 + /** 429 + * cpu_wakeup_latency_qos_limit - Current CPU system wakeup latency QoS limit. 430 + * 431 + * Returns the current CPU system wakeup latency QoS limit that may have been 432 + * requested by user space. 433 + */ 434 + s32 cpu_wakeup_latency_qos_limit(void) 435 + { 436 + return pm_qos_read_value(&cpu_wakeup_latency_constraints); 437 + } 438 + 439 + static int cpu_wakeup_latency_qos_open(struct inode *inode, struct file *filp) 440 + { 441 + struct pm_qos_request *req; 442 + 443 + req = kzalloc(sizeof(*req), GFP_KERNEL); 444 + if (!req) 445 + return -ENOMEM; 446 + 447 + req->qos = &cpu_wakeup_latency_constraints; 448 + pm_qos_update_target(req->qos, &req->node, PM_QOS_ADD_REQ, 449 + PM_QOS_RESUME_LATENCY_NO_CONSTRAINT); 450 + filp->private_data = req; 451 + 452 + return 0; 453 + } 454 + 455 + static int cpu_wakeup_latency_qos_release(struct inode *inode, 456 + struct file *filp) 457 + { 458 + struct pm_qos_request *req = filp->private_data; 459 + 460 + filp->private_data = NULL; 461 + pm_qos_update_target(req->qos, &req->node, PM_QOS_REMOVE_REQ, 462 + PM_QOS_RESUME_LATENCY_NO_CONSTRAINT); 463 + kfree(req); 464 + 465 + return 0; 466 + } 467 + 468 + static ssize_t cpu_wakeup_latency_qos_read(struct file *filp, char __user *buf, 469 + size_t count, loff_t *f_pos) 470 + { 471 + s32 value = pm_qos_read_value(&cpu_wakeup_latency_constraints); 472 + 473 + return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32)); 474 + } 475 + 476 + static ssize_t cpu_wakeup_latency_qos_write(struct file *filp, 477 + const char __user *buf, 478 + size_t count, loff_t *f_pos) 479 + { 480 + struct pm_qos_request *req = filp->private_data; 481 + s32 value; 482 + 483 + if (count == sizeof(s32)) { 484 + if (copy_from_user(&value, buf, sizeof(s32))) 485 + return -EFAULT; 486 + } else { 487 + int ret; 488 + 489 + ret = kstrtos32_from_user(buf, count, 16, &value); 490 + if (ret) 491 + return ret; 492 + } 493 + 494 + if (value < 0) 495 + return -EINVAL; 496 + 497 + pm_qos_update_target(req->qos, &req->node, PM_QOS_UPDATE_REQ, value); 498 + 499 + return count; 500 + } 501 + 502 + static const struct file_operations cpu_wakeup_latency_qos_fops = { 503 + .open = cpu_wakeup_latency_qos_open, 504 + .release = cpu_wakeup_latency_qos_release, 505 + .read = cpu_wakeup_latency_qos_read, 506 + .write = cpu_wakeup_latency_qos_write, 507 + .llseek = noop_llseek, 508 + }; 509 + 510 + static struct miscdevice cpu_wakeup_latency_qos_miscdev = { 511 + .minor = MISC_DYNAMIC_MINOR, 512 + .name = "cpu_wakeup_latency", 513 + .fops = &cpu_wakeup_latency_qos_fops, 514 + }; 515 + #endif /* CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP */ 516 + 418 517 static int __init cpu_latency_qos_init(void) 419 518 { 420 519 int ret; ··· 522 423 if (ret < 0) 523 424 pr_err("%s: %s setup failed\n", __func__, 524 425 cpu_latency_qos_miscdev.name); 426 + 427 + #ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP 428 + ret = misc_register(&cpu_wakeup_latency_qos_miscdev); 429 + if (ret < 0) 430 + pr_err("%s: %s setup failed\n", __func__, 431 + cpu_wakeup_latency_qos_miscdev.name); 432 + #endif 525 433 526 434 return ret; 527 435 }