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

PM / Runtime: Use device PM QoS constraints (v2)

Make the runtime PM core use device PM QoS constraints to check if
it is allowed to suspend a given device, so that an error code is
returned if the device's own PM QoS constraint is negative or one of
its children has already been suspended for too long. If this is
not the case, the maximum estimated time the device is allowed to be
suspended, computed as the minimum of the device's PM QoS constraint
and the PM QoS constraints of its children (reduced by the difference
between the current time and their suspend times) is stored in a new
device's PM field power.max_time_suspended_ns that can be used by
the device's subsystem or PM domain to decide whether or not to put
the device into lower-power (and presumably higher-latency) states
later (if the constraint is 0, which means "no constraint", the
power.max_time_suspended_ns is set to -1).

Additionally, the time of execution of the subsystem-level
.runtime_suspend() callback for the device is recorded in the new
power.suspend_time field for later use by the device's subsystem or
PM domain along with power.max_time_suspended_ns (it also is used
by the core code when the device's parent is suspended).

Introduce a new helper function,
pm_runtime_update_max_time_suspended(), allowing subsystems and PM
domains (or device drivers) to update the power.max_time_suspended_ns
field, for example after changing the power state of a suspended
device.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

+154 -28
+16 -8
drivers/base/power/qos.c
··· 47 47 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); 48 48 49 49 /** 50 - * dev_pm_qos_read_value - Get PM QoS constraint for a given device. 50 + * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. 51 + * @dev: Device to get the PM QoS constraint value for. 52 + * 53 + * This routine must be called with dev->power.lock held. 54 + */ 55 + s32 __dev_pm_qos_read_value(struct device *dev) 56 + { 57 + struct pm_qos_constraints *c = dev->power.constraints; 58 + 59 + return c ? pm_qos_read_value(c) : 0; 60 + } 61 + 62 + /** 63 + * dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked). 51 64 * @dev: Device to get the PM QoS constraint value for. 52 65 */ 53 66 s32 dev_pm_qos_read_value(struct device *dev) 54 67 { 55 - struct pm_qos_constraints *c; 56 68 unsigned long flags; 57 - s32 ret = 0; 69 + s32 ret; 58 70 59 71 spin_lock_irqsave(&dev->power.lock, flags); 60 - 61 - c = dev->power.constraints; 62 - if (c) 63 - ret = pm_qos_read_value(c); 64 - 72 + ret = __dev_pm_qos_read_value(dev); 65 73 spin_unlock_irqrestore(&dev->power.lock, flags); 66 74 67 75 return ret;
+128 -20
drivers/base/power/runtime.c
··· 279 279 return retval != -EACCES ? retval : -EIO; 280 280 } 281 281 282 + struct rpm_qos_data { 283 + ktime_t time_now; 284 + s64 constraint_ns; 285 + }; 286 + 287 + /** 288 + * rpm_update_qos_constraint - Update a given PM QoS constraint data. 289 + * @dev: Device whose timing data to use. 290 + * @data: PM QoS constraint data to update. 291 + * 292 + * Use the suspend timing data of @dev to update PM QoS constraint data pointed 293 + * to by @data. 294 + */ 295 + static int rpm_update_qos_constraint(struct device *dev, void *data) 296 + { 297 + struct rpm_qos_data *qos = data; 298 + unsigned long flags; 299 + s64 delta_ns; 300 + int ret = 0; 301 + 302 + spin_lock_irqsave(&dev->power.lock, flags); 303 + 304 + if (dev->power.max_time_suspended_ns < 0) 305 + goto out; 306 + 307 + delta_ns = dev->power.max_time_suspended_ns - 308 + ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time)); 309 + if (delta_ns <= 0) { 310 + ret = -EBUSY; 311 + goto out; 312 + } 313 + 314 + if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0) 315 + qos->constraint_ns = delta_ns; 316 + 317 + out: 318 + spin_unlock_irqrestore(&dev->power.lock, flags); 319 + 320 + return ret; 321 + } 322 + 282 323 /** 283 324 * rpm_suspend - Carry out runtime suspend of given device. 284 325 * @dev: Device to suspend. ··· 346 305 { 347 306 int (*callback)(struct device *); 348 307 struct device *parent = NULL; 308 + struct rpm_qos_data qos; 349 309 int retval; 350 310 351 311 trace_rpm_suspend(dev, rpmflags); ··· 442 400 goto out; 443 401 } 444 402 403 + qos.constraint_ns = __dev_pm_qos_read_value(dev); 404 + if (qos.constraint_ns < 0) { 405 + /* Negative constraint means "never suspend". */ 406 + retval = -EPERM; 407 + goto out; 408 + } 409 + qos.constraint_ns *= NSEC_PER_USEC; 410 + qos.time_now = ktime_get(); 411 + 445 412 __update_runtime_status(dev, RPM_SUSPENDING); 413 + 414 + if (!dev->power.ignore_children) { 415 + if (dev->power.irq_safe) 416 + spin_unlock(&dev->power.lock); 417 + else 418 + spin_unlock_irq(&dev->power.lock); 419 + 420 + retval = device_for_each_child(dev, &qos, 421 + rpm_update_qos_constraint); 422 + 423 + if (dev->power.irq_safe) 424 + spin_lock(&dev->power.lock); 425 + else 426 + spin_lock_irq(&dev->power.lock); 427 + 428 + if (retval) 429 + goto fail; 430 + } 431 + 432 + dev->power.suspend_time = qos.time_now; 433 + dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1; 446 434 447 435 if (dev->pm_domain) 448 436 callback = dev->pm_domain->ops.runtime_suspend; ··· 486 414 callback = NULL; 487 415 488 416 retval = rpm_callback(callback, dev); 489 - if (retval) { 490 - __update_runtime_status(dev, RPM_ACTIVE); 491 - dev->power.deferred_resume = false; 492 - if (retval == -EAGAIN || retval == -EBUSY) { 493 - dev->power.runtime_error = 0; 417 + if (retval) 418 + goto fail; 494 419 495 - /* 496 - * If the callback routine failed an autosuspend, and 497 - * if the last_busy time has been updated so that there 498 - * is a new autosuspend expiration time, automatically 499 - * reschedule another autosuspend. 500 - */ 501 - if ((rpmflags & RPM_AUTO) && 502 - pm_runtime_autosuspend_expiration(dev) != 0) 503 - goto repeat; 504 - } else { 505 - pm_runtime_cancel_pending(dev); 506 - } 507 - wake_up_all(&dev->power.wait_queue); 508 - goto out; 509 - } 510 420 no_callback: 511 421 __update_runtime_status(dev, RPM_SUSPENDED); 512 422 pm_runtime_deactivate_timer(dev); ··· 520 466 trace_rpm_return_int(dev, _THIS_IP_, retval); 521 467 522 468 return retval; 469 + 470 + fail: 471 + __update_runtime_status(dev, RPM_ACTIVE); 472 + dev->power.suspend_time = ktime_set(0, 0); 473 + dev->power.max_time_suspended_ns = -1; 474 + dev->power.deferred_resume = false; 475 + if (retval == -EAGAIN || retval == -EBUSY) { 476 + dev->power.runtime_error = 0; 477 + 478 + /* 479 + * If the callback routine failed an autosuspend, and 480 + * if the last_busy time has been updated so that there 481 + * is a new autosuspend expiration time, automatically 482 + * reschedule another autosuspend. 483 + */ 484 + if ((rpmflags & RPM_AUTO) && 485 + pm_runtime_autosuspend_expiration(dev) != 0) 486 + goto repeat; 487 + } else { 488 + pm_runtime_cancel_pending(dev); 489 + } 490 + wake_up_all(&dev->power.wait_queue); 491 + goto out; 523 492 } 524 493 525 494 /** ··· 696 619 697 620 if (dev->power.no_callbacks) 698 621 goto no_callback; /* Assume success. */ 622 + 623 + dev->power.suspend_time = ktime_set(0, 0); 624 + dev->power.max_time_suspended_ns = -1; 699 625 700 626 __update_runtime_status(dev, RPM_RESUMING); 701 627 ··· 1359 1279 setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, 1360 1280 (unsigned long)dev); 1361 1281 1282 + dev->power.suspend_time = ktime_set(0, 0); 1283 + dev->power.max_time_suspended_ns = -1; 1284 + 1362 1285 init_waitqueue_head(&dev->power.wait_queue); 1363 1286 } 1364 1287 ··· 1378 1295 pm_runtime_set_suspended(dev); 1379 1296 if (dev->power.irq_safe && dev->parent) 1380 1297 pm_runtime_put_sync(dev->parent); 1298 + } 1299 + 1300 + /** 1301 + * pm_runtime_update_max_time_suspended - Update device's suspend time data. 1302 + * @dev: Device to handle. 1303 + * @delta_ns: Value to subtract from the device's max_time_suspended_ns field. 1304 + * 1305 + * Update the device's power.max_time_suspended_ns field by subtracting 1306 + * @delta_ns from it. The resulting value of power.max_time_suspended_ns is 1307 + * never negative. 1308 + */ 1309 + void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns) 1310 + { 1311 + unsigned long flags; 1312 + 1313 + spin_lock_irqsave(&dev->power.lock, flags); 1314 + 1315 + if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) { 1316 + if (dev->power.max_time_suspended_ns > delta_ns) 1317 + dev->power.max_time_suspended_ns -= delta_ns; 1318 + else 1319 + dev->power.max_time_suspended_ns = 0; 1320 + } 1321 + 1322 + spin_unlock_irqrestore(&dev->power.lock, flags); 1381 1323 }
+2
include/linux/pm.h
··· 521 521 unsigned long active_jiffies; 522 522 unsigned long suspended_jiffies; 523 523 unsigned long accounting_timestamp; 524 + ktime_t suspend_time; 525 + s64 max_time_suspended_ns; 524 526 #endif 525 527 struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ 526 528 struct pm_qos_constraints *constraints;
+3
include/linux/pm_qos.h
··· 78 78 int pm_qos_request_active(struct pm_qos_request *req); 79 79 s32 pm_qos_read_value(struct pm_qos_constraints *c); 80 80 81 + s32 __dev_pm_qos_read_value(struct device *dev); 81 82 s32 dev_pm_qos_read_value(struct device *dev); 82 83 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, 83 84 s32 value); ··· 120 119 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c) 121 120 { return 0; } 122 121 122 + static inline s32 __dev_pm_qos_read_value(struct device *dev) 123 + { return 0; } 123 124 static inline s32 dev_pm_qos_read_value(struct device *dev) 124 125 { return 0; } 125 126 static inline int dev_pm_qos_add_request(struct device *dev,
+5
include/linux/pm_runtime.h
··· 45 45 extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); 46 46 extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); 47 47 extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); 48 + extern void pm_runtime_update_max_time_suspended(struct device *dev, 49 + s64 delta_ns); 48 50 49 51 static inline bool pm_children_suspended(struct device *dev) 50 52 { ··· 149 147 int delay) {} 150 148 static inline unsigned long pm_runtime_autosuspend_expiration( 151 149 struct device *dev) { return 0; } 150 + 151 + static inline void pm_runtime_update_max_time_suspended(struct device *dev, 152 + s64 delta_ns) {} 152 153 153 154 #endif /* !CONFIG_PM_RUNTIME */ 154 155