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

Merge branches 'pm-qos' and 'pm-tools'

Merge PM QoS updates and a cpupower utility update for 6.19-rc1:

- Introduce and document a QoS limit on CPU exit latency during wakeup
from suspend-to-idle (Ulf Hansson)

- Add support for building libcpupower statically (Zuo An)

* pm-qos:
Documentation: power/cpuidle: Document the CPU system wakeup latency QoS
cpuidle: Respect the CPU system wakeup QoS limit for cpuidle
sched: idle: Respect the CPU system wakeup QoS limit for s2idle
pmdomain: Respect the CPU system wakeup QoS limit for cpuidle
pmdomain: Respect the CPU system wakeup QoS limit for s2idle
PM: QoS: Introduce a CPU system wakeup QoS limit

* pm-tools:
tools/power/cpupower: Support building libcpupower statically

+224 -30
+9
Documentation/admin-guide/pm/cpuidle.rst
··· 580 580 they are allowed to select for that CPU. They should never select any idle 581 581 states with exit latency beyond that limit. 582 582 583 + While the above CPU QoS constraints apply to CPU idle time management, user 584 + space may also request a CPU system wakeup latency QoS limit, via the 585 + `cpu_wakeup_latency` file. This QoS constraint is respected when selecting a 586 + suitable idle state for the CPUs, while entering the system-wide suspend-to-idle 587 + sleep state, but also to the regular CPU idle time management. 588 + 589 + Note that, the management of the `cpu_wakeup_latency` file works according to 590 + the 'cpu_dma_latency' file from user space point of view. Moreover, the unit 591 + is also microseconds. 583 592 584 593 Idle States Control Via Kernel Command Line 585 594 ===========================================
+5 -4
Documentation/power/pm_qos_interface.rst
··· 55 55 56 56 From user space: 57 57 58 - The infrastructure exposes one device node, /dev/cpu_dma_latency, for the CPU 58 + The infrastructure exposes two separate device nodes, /dev/cpu_dma_latency for 59 + the CPU latency QoS and /dev/cpu_wakeup_latency for the CPU system wakeup 59 60 latency QoS. 60 61 61 62 Only processes can register a PM QoS request. To provide for automatic ··· 64 63 parameter requests as follows. 65 64 66 65 To register the default PM QoS target for the CPU latency QoS, the process must 67 - open /dev/cpu_dma_latency. 66 + open /dev/cpu_dma_latency. To register a CPU system wakeup QoS limit, the 67 + process must open /dev/cpu_wakeup_latency. 68 68 69 69 As long as the device node is held open that process has a registered 70 70 request on the parameter. 71 71 72 72 To change the requested target value, the process needs to write an s32 value to 73 73 the open device node. Alternatively, it can write a hex string for the value 74 - using the 10 char long format e.g. "0x12345678". This translates to a 75 - cpu_latency_qos_update_request() call. 74 + using the 10 char long format e.g. "0x12345678". 76 75 77 76 To remove the user mode request for a target value simply close the device 78 77 node.
+7 -5
drivers/cpuidle/cpuidle.c
··· 184 184 * cpuidle_enter_s2idle - Enter an idle state suitable for suspend-to-idle. 185 185 * @drv: cpuidle driver for the given CPU. 186 186 * @dev: cpuidle device for the given CPU. 187 + * @latency_limit_ns: Idle state exit latency limit 187 188 * 188 189 * If there are states with the ->enter_s2idle callback, find the deepest of 189 190 * them and enter it with frozen tick. 190 191 */ 191 - int cpuidle_enter_s2idle(struct cpuidle_driver *drv, struct cpuidle_device *dev) 192 + int cpuidle_enter_s2idle(struct cpuidle_driver *drv, struct cpuidle_device *dev, 193 + u64 latency_limit_ns) 192 194 { 193 195 int index; 194 196 195 197 /* 196 - * Find the deepest state with ->enter_s2idle present, which guarantees 197 - * that interrupts won't be enabled when it exits and allows the tick to 198 - * be frozen safely. 198 + * Find the deepest state with ->enter_s2idle present that meets the 199 + * specified latency limit, which guarantees that interrupts won't be 200 + * enabled when it exits and allows the tick to be frozen safely. 199 201 */ 200 - index = find_deepest_state(drv, dev, U64_MAX, 0, true); 202 + index = find_deepest_state(drv, dev, latency_limit_ns, 0, true); 201 203 if (index > 0) { 202 204 enter_s2idle_proper(drv, dev, index); 203 205 local_irq_enable();
+4
drivers/cpuidle/governor.c
··· 111 111 struct device *device = get_cpu_device(cpu); 112 112 int device_req = dev_pm_qos_raw_resume_latency(device); 113 113 int global_req = cpu_latency_qos_limit(); 114 + int global_wake_req = cpu_wakeup_latency_qos_limit(); 115 + 116 + if (global_req > global_wake_req) 117 + global_req = global_wake_req; 114 118 115 119 if (device_req > global_req) 116 120 device_req = global_req;
+8 -2
drivers/pmdomain/core.c
··· 1425 1425 return; 1426 1426 } 1427 1427 1428 - /* Choose the deepest state when suspending */ 1429 - genpd->state_idx = genpd->state_count - 1; 1428 + if (genpd->gov && genpd->gov->system_power_down_ok) { 1429 + if (!genpd->gov->system_power_down_ok(&genpd->domain)) 1430 + return; 1431 + } else { 1432 + /* Default to the deepest state. */ 1433 + genpd->state_idx = genpd->state_count - 1; 1434 + } 1435 + 1430 1436 if (_genpd_power_off(genpd, false)) { 1431 1437 genpd->states[genpd->state_idx].rejected++; 1432 1438 return;
+32 -1
drivers/pmdomain/governor.c
··· 351 351 ktime_t domain_wakeup, next_hrtimer; 352 352 ktime_t now = ktime_get(); 353 353 struct device *cpu_dev; 354 - s64 cpu_constraint, global_constraint; 354 + s64 cpu_constraint, global_constraint, wakeup_constraint; 355 355 s64 idle_duration_ns; 356 356 int cpu, i; 357 357 ··· 362 362 if (!(genpd->flags & GENPD_FLAG_CPU_DOMAIN)) 363 363 return true; 364 364 365 + wakeup_constraint = cpu_wakeup_latency_qos_limit(); 365 366 global_constraint = cpu_latency_qos_limit(); 367 + if (global_constraint > wakeup_constraint) 368 + global_constraint = wakeup_constraint; 369 + 366 370 /* 367 371 * Find the next wakeup for any of the online CPUs within the PM domain 368 372 * and its subdomains. Note, we only need the genpd->cpus, as it already ··· 419 415 return false; 420 416 } 421 417 418 + static bool cpu_system_power_down_ok(struct dev_pm_domain *pd) 419 + { 420 + s64 constraint_ns = cpu_wakeup_latency_qos_limit() * NSEC_PER_USEC; 421 + struct generic_pm_domain *genpd = pd_to_genpd(pd); 422 + int state_idx = genpd->state_count - 1; 423 + 424 + if (!(genpd->flags & GENPD_FLAG_CPU_DOMAIN)) { 425 + genpd->state_idx = state_idx; 426 + return true; 427 + } 428 + 429 + /* Find the deepest state for the latency constraint. */ 430 + while (state_idx >= 0) { 431 + s64 latency_ns = genpd->states[state_idx].power_off_latency_ns + 432 + genpd->states[state_idx].power_on_latency_ns; 433 + 434 + if (latency_ns <= constraint_ns) { 435 + genpd->state_idx = state_idx; 436 + return true; 437 + } 438 + state_idx--; 439 + } 440 + 441 + return false; 442 + } 443 + 422 444 struct dev_power_governor pm_domain_cpu_gov = { 423 445 .suspend_ok = default_suspend_ok, 424 446 .power_down_ok = cpu_power_down_ok, 447 + .system_power_down_ok = cpu_system_power_down_ok, 425 448 }; 426 449 #endif 427 450
+4 -2
include/linux/cpuidle.h
··· 248 248 struct cpuidle_device *dev, 249 249 u64 latency_limit_ns); 250 250 extern int cpuidle_enter_s2idle(struct cpuidle_driver *drv, 251 - struct cpuidle_device *dev); 251 + struct cpuidle_device *dev, 252 + u64 latency_limit_ns); 252 253 extern void cpuidle_use_deepest_state(u64 latency_limit_ns); 253 254 #else 254 255 static inline int cpuidle_find_deepest_state(struct cpuidle_driver *drv, ··· 257 256 u64 latency_limit_ns) 258 257 {return -ENODEV; } 259 258 static inline int cpuidle_enter_s2idle(struct cpuidle_driver *drv, 260 - struct cpuidle_device *dev) 259 + struct cpuidle_device *dev, 260 + u64 latency_limit_ns) 261 261 {return -ENODEV; } 262 262 static inline void cpuidle_use_deepest_state(u64 latency_limit_ns) 263 263 {
+1
include/linux/pm_domain.h
··· 153 153 }; 154 154 155 155 struct dev_power_governor { 156 + bool (*system_power_down_ok)(struct dev_pm_domain *domain); 156 157 bool (*power_down_ok)(struct dev_pm_domain *domain); 157 158 bool (*suspend_ok)(struct device *dev); 158 159 };
+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 }
+7 -5
kernel/sched/idle.c
··· 131 131 } 132 132 133 133 static int call_cpuidle_s2idle(struct cpuidle_driver *drv, 134 - struct cpuidle_device *dev) 134 + struct cpuidle_device *dev, 135 + u64 max_latency_ns) 135 136 { 136 137 if (current_clr_polling_and_test()) 137 138 return -EBUSY; 138 139 139 - return cpuidle_enter_s2idle(drv, dev); 140 + return cpuidle_enter_s2idle(drv, dev, max_latency_ns); 140 141 } 141 142 142 143 static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev, ··· 206 205 u64 max_latency_ns; 207 206 208 207 if (idle_should_enter_s2idle()) { 208 + max_latency_ns = cpu_wakeup_latency_qos_limit() * 209 + NSEC_PER_USEC; 209 210 210 - entered_state = call_cpuidle_s2idle(drv, dev); 211 + entered_state = call_cpuidle_s2idle(drv, dev, 212 + max_latency_ns); 211 213 if (entered_state > 0) 212 214 goto exit_idle; 213 - 214 - max_latency_ns = U64_MAX; 215 215 } else { 216 216 max_latency_ns = dev->forced_idle_latency_limit_ns; 217 217 }
+21 -11
tools/power/cpupower/Makefile
··· 37 37 # cpufreq-bench benchmarking tool 38 38 CPUFREQ_BENCH ?= true 39 39 40 - # Do not build libraries, but build the code in statically 41 - # Libraries are still built, otherwise the Makefile code would 42 - # be rather ugly. 40 + # Build the code, including libraries, statically. 43 41 export STATIC ?= false 44 42 45 43 # Prefix to the directories we're installing to ··· 205 207 $(ECHO) " CC " $@ 206 208 $(QUIET) $(CC) $(CFLAGS) -fPIC -o $@ -c lib/$*.c 207 209 208 - $(OUTPUT)libcpupower.so.$(LIB_VER): $(LIB_OBJS) 210 + ifeq ($(strip $(STATIC)),true) 211 + LIBCPUPOWER := libcpupower.a 212 + else 213 + LIBCPUPOWER := libcpupower.so.$(LIB_VER) 214 + endif 215 + 216 + $(OUTPUT)$(LIBCPUPOWER): $(LIB_OBJS) 217 + ifeq ($(strip $(STATIC)),true) 218 + $(ECHO) " AR " $@ 219 + $(QUIET) $(AR) rcs $@ $(LIB_OBJS) 220 + else 209 221 $(ECHO) " LD " $@ 210 222 $(QUIET) $(CC) -shared $(CFLAGS) $(LDFLAGS) -o $@ \ 211 223 -Wl,-soname,libcpupower.so.$(LIB_MAJ) $(LIB_OBJS) 212 224 @ln -sf $(@F) $(OUTPUT)libcpupower.so 213 225 @ln -sf $(@F) $(OUTPUT)libcpupower.so.$(LIB_MAJ) 226 + endif 214 227 215 - libcpupower: $(OUTPUT)libcpupower.so.$(LIB_VER) 228 + libcpupower: $(OUTPUT)$(LIBCPUPOWER) 216 229 217 230 # Let all .o files depend on its .c file and all headers 218 231 # Might be worth to put this into utils/Makefile at some point of time ··· 233 224 $(ECHO) " CC " $@ 234 225 $(QUIET) $(CC) $(CFLAGS) -I./lib -I ./utils -o $@ -c $*.c 235 226 236 - $(OUTPUT)cpupower: $(UTIL_OBJS) $(OUTPUT)libcpupower.so.$(LIB_VER) 227 + $(OUTPUT)cpupower: $(UTIL_OBJS) $(OUTPUT)$(LIBCPUPOWER) 237 228 $(ECHO) " CC " $@ 238 229 ifeq ($(strip $(STATIC)),true) 239 230 $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lrt -lpci -L$(OUTPUT) -o $@ ··· 278 269 done; 279 270 endif 280 271 281 - compile-bench: $(OUTPUT)libcpupower.so.$(LIB_VER) 272 + compile-bench: $(OUTPUT)$(LIBCPUPOWER) 282 273 @V=$(V) confdir=$(confdir) $(MAKE) -C bench O=$(OUTPUT) 283 274 284 275 # we compile into subdirectories. if the target directory is not the ··· 296 287 -find $(OUTPUT) \( -not -type d \) -and \( -name '*~' -o -name '*.[oas]' \) -type f -print \ 297 288 | xargs rm -f 298 289 -rm -f $(OUTPUT)cpupower 290 + -rm -f $(OUTPUT)libcpupower.a 299 291 -rm -f $(OUTPUT)libcpupower.so* 300 292 -rm -rf $(OUTPUT)po/*.gmo 301 293 -rm -rf $(OUTPUT)po/*.pot ··· 305 295 306 296 install-lib: libcpupower 307 297 $(INSTALL) -d $(DESTDIR)${libdir} 298 + ifeq ($(strip $(STATIC)),true) 299 + $(CP) $(OUTPUT)libcpupower.a $(DESTDIR)${libdir}/ 300 + else 308 301 $(CP) $(OUTPUT)libcpupower.so* $(DESTDIR)${libdir}/ 302 + endif 309 303 $(INSTALL) -d $(DESTDIR)${includedir} 310 304 $(INSTALL_DATA) lib/cpufreq.h $(DESTDIR)${includedir}/cpufreq.h 311 305 $(INSTALL_DATA) lib/cpuidle.h $(DESTDIR)${includedir}/cpuidle.h ··· 350 336 @#DESTDIR must be set from outside to survive 351 337 @sbindir=$(sbindir) bindir=$(bindir) docdir=$(docdir) confdir=$(confdir) $(MAKE) -C bench O=$(OUTPUT) install 352 338 353 - ifeq ($(strip $(STATIC)),true) 354 - install: all install-tools install-man $(INSTALL_NLS) $(INSTALL_BENCH) 355 - else 356 339 install: all install-lib install-tools install-man $(INSTALL_NLS) $(INSTALL_BENCH) 357 - endif 358 340 359 341 uninstall: 360 342 - rm -f $(DESTDIR)${libdir}/libcpupower.*