From 8a62ffe2753a845272f4f2100b5fca0b6053ff6f Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Fri, 21 Dec 2018 11:33:54 +0100 Subject: PM-runtime: Add new interface to get accounted time Some drivers (like i915/drm) needs to get the accounted suspended time. pm_runtime_suspended_time() will return the suspended accounted time in ns unit. Reviewed-by: Ulf Hansson Signed-off-by: Vincent Guittot Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/base/power') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 457be03b744d..a453090c9449 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -88,6 +88,21 @@ static void __update_runtime_status(struct device *dev, enum rpm_status status) dev->power.runtime_status = status; } +u64 pm_runtime_suspended_time(struct device *dev) +{ + unsigned long flags, time; + + spin_lock_irqsave(&dev->power.lock, flags); + + update_pm_runtime_accounting(dev); + time = dev->power.suspended_jiffies; + + spin_unlock_irqrestore(&dev->power.lock, flags); + + return jiffies_to_nsecs(time); +} +EXPORT_SYMBOL_GPL(pm_runtime_suspended_time); + /** * pm_runtime_deactivate_timer - Deactivate given device's suspend timer. * @dev: Device to handle. -- cgit v1.2.3-59-g8ed1b From 40619f7dd3ef05ae7861bc60d401585d316e1374 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Sat, 5 Jan 2019 13:58:45 -0600 Subject: PM: clock_ops: fix missing clk_prepare() return value check clk_prepare() can fail, so check its status and if it fails, issue an error message and change the clock_entry_status to PCE_STATUS_ERROR. Signed-off-by: Aditya Pakki [ rjw: Subject ] Signed-off-by: Rafael J. Wysocki --- drivers/base/power/clock_ops.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 5a42ae4078c2..365ad751ce0f 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -65,10 +65,15 @@ static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce) if (IS_ERR(ce->clk)) { ce->status = PCE_STATUS_ERROR; } else { - clk_prepare(ce->clk); - ce->status = PCE_STATUS_ACQUIRED; - dev_dbg(dev, "Clock %pC con_id %s managed by runtime PM.\n", - ce->clk, ce->con_id); + if (clk_prepare(ce->clk)) { + ce->status = PCE_STATUS_ERROR; + dev_err(dev, "clk_prepare() failed\n"); + } else { + ce->status = PCE_STATUS_ACQUIRED; + dev_dbg(dev, + "Clock %pC con_id %s managed by runtime PM.\n", + ce->clk, ce->con_id); + } } } -- cgit v1.2.3-59-g8ed1b From 58456488e0e3793f2f95a3d2e2a78b6eba0ad0aa Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Wed, 23 Jan 2019 08:50:13 +0100 Subject: PM-runtime: update accounting_timestamp on enable Initializing accounting_timestamp to something different from 0 during pm_runtime_init() doesn't make sense and puts an artificial ordering constraint between timekeeping_init() and pm_runtime_init(). PM-runtime should start time accounting only when it is enabled and discard the period when disabled. Set accounting_timestamp to now when enabling PM-runtime. Suggested-by: "Rafael J. Wysocki" Signed-off-by: Vincent Guittot Reviewed-by: Ulf Hansson [ rjw: Subject & changelog ] Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index a453090c9449..f23b7ecfce3b 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1309,10 +1309,15 @@ void pm_runtime_enable(struct device *dev) spin_lock_irqsave(&dev->power.lock, flags); - if (dev->power.disable_depth > 0) + if (dev->power.disable_depth > 0) { dev->power.disable_depth--; - else + + /* About to enable runtime pm, set accounting_timestamp to now */ + if (!dev->power.disable_depth) + dev->power.accounting_timestamp = jiffies; + } else { dev_warn(dev, "Unbalanced %s!\n", __func__); + } WARN(!dev->power.disable_depth && dev->power.runtime_status == RPM_SUSPENDED && @@ -1509,7 +1514,7 @@ void pm_runtime_init(struct device *dev) dev->power.request_pending = false; dev->power.request = RPM_REQ_NONE; dev->power.deferred_resume = false; - dev->power.accounting_timestamp = jiffies; + dev->power.accounting_timestamp = 0; INIT_WORK(&dev->power.work, pm_runtime_work); dev->power.timer_expires = 0; -- cgit v1.2.3-59-g8ed1b From a08c2a5a31941131c41feaa0429e4c8854cf48f2 Mon Sep 17 00:00:00 2001 From: Thara Gopinath Date: Wed, 23 Jan 2019 08:50:14 +0100 Subject: PM-runtime: Replace jiffies-based accounting with ktime-based accounting Replace jiffies-based accounting for runtime_active_time and runtime_suspended_time with ktime-based accounting. This makes the runtime debug counters inline with genpd and other PM subsytems which use ktime-based accounting. Timekeeping is initialized before driver_init(). It's only at that time that PM-runtime can be enabled. Signed-off-by: Thara Gopinath [switch from ktime to raw nsec] Signed-off-by: Vincent Guittot Reviewed-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 17 +++++++++-------- drivers/base/power/sysfs.c | 11 ++++++++--- include/linux/pm.h | 6 +++--- 3 files changed, 20 insertions(+), 14 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index f23b7ecfce3b..eb1a3b878e1e 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -66,8 +66,8 @@ static int rpm_suspend(struct device *dev, int rpmflags); */ void update_pm_runtime_accounting(struct device *dev) { - unsigned long now = jiffies; - unsigned long delta; + u64 now = ktime_to_ns(ktime_get()); + u64 delta; delta = now - dev->power.accounting_timestamp; @@ -77,9 +77,9 @@ void update_pm_runtime_accounting(struct device *dev) return; if (dev->power.runtime_status == RPM_SUSPENDED) - dev->power.suspended_jiffies += delta; + dev->power.suspended_time += delta; else - dev->power.active_jiffies += delta; + dev->power.active_time += delta; } static void __update_runtime_status(struct device *dev, enum rpm_status status) @@ -90,16 +90,17 @@ static void __update_runtime_status(struct device *dev, enum rpm_status status) u64 pm_runtime_suspended_time(struct device *dev) { - unsigned long flags, time; + u64 time; + unsigned long flags; spin_lock_irqsave(&dev->power.lock, flags); update_pm_runtime_accounting(dev); - time = dev->power.suspended_jiffies; + time = dev->power.suspended_time; spin_unlock_irqrestore(&dev->power.lock, flags); - return jiffies_to_nsecs(time); + return time; } EXPORT_SYMBOL_GPL(pm_runtime_suspended_time); @@ -1314,7 +1315,7 @@ void pm_runtime_enable(struct device *dev) /* About to enable runtime pm, set accounting_timestamp to now */ if (!dev->power.disable_depth) - dev->power.accounting_timestamp = jiffies; + dev->power.accounting_timestamp = ktime_to_ns(ktime_get()); } else { dev_warn(dev, "Unbalanced %s!\n", __func__); } diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index d713738ce796..96c8a227610a 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -125,9 +125,12 @@ static ssize_t runtime_active_time_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; + u64 tmp; spin_lock_irq(&dev->power.lock); update_pm_runtime_accounting(dev); - ret = sprintf(buf, "%i\n", jiffies_to_msecs(dev->power.active_jiffies)); + tmp = dev->power.active_time; + do_div(tmp, NSEC_PER_MSEC); + ret = sprintf(buf, "%llu\n", tmp); spin_unlock_irq(&dev->power.lock); return ret; } @@ -138,10 +141,12 @@ static ssize_t runtime_suspended_time_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; + u64 tmp; spin_lock_irq(&dev->power.lock); update_pm_runtime_accounting(dev); - ret = sprintf(buf, "%i\n", - jiffies_to_msecs(dev->power.suspended_jiffies)); + tmp = dev->power.suspended_time; + do_div(tmp, NSEC_PER_MSEC); + ret = sprintf(buf, "%llu\n", tmp); spin_unlock_irq(&dev->power.lock); return ret; } diff --git a/include/linux/pm.h b/include/linux/pm.h index 0bd9de116826..3d2cbf947768 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -633,9 +633,9 @@ struct dev_pm_info { int runtime_error; int autosuspend_delay; u64 last_busy; - unsigned long active_jiffies; - unsigned long suspended_jiffies; - unsigned long accounting_timestamp; + u64 active_time; + u64 suspended_time; + u64 accounting_timestamp; #endif struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ void (*set_latency_tolerance)(struct device *, s32); -- cgit v1.2.3-59-g8ed1b From f800ea320c09fbc8bf15aecd5c05e8e6b27ae64e Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 30 Jan 2019 22:40:17 +0100 Subject: PM-runtime: Optimize pm_runtime_autosuspend_expiration() pm_runtime_autosuspend_expiration calls ktime_get_mono_fast_ns() even when its returned value may be unused. Therefore get the current time later and remove gotos while there. Signed-off-by: Ladislav Michl Acked-by: Tony Lindgren Acked-by: Vincent Guittot Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 4eaf166d7de1..1caa3944898f 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -145,24 +145,21 @@ static void pm_runtime_cancel_pending(struct device *dev) u64 pm_runtime_autosuspend_expiration(struct device *dev) { int autosuspend_delay; - u64 last_busy, expires = 0; - u64 now = ktime_get_mono_fast_ns(); + u64 expires; if (!dev->power.use_autosuspend) - goto out; + return 0; autosuspend_delay = READ_ONCE(dev->power.autosuspend_delay); if (autosuspend_delay < 0) - goto out; - - last_busy = READ_ONCE(dev->power.last_busy); + return 0; - expires = last_busy + (u64)autosuspend_delay * NSEC_PER_MSEC; - if (expires <= now) - expires = 0; /* Already expired. */ + expires = READ_ONCE(dev->power.last_busy); + expires += (u64)autosuspend_delay * NSEC_PER_MSEC; + if (expires > ktime_get_mono_fast_ns()) + return expires; /* Expires in the future */ - out: - return expires; + return 0; } EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration); -- cgit v1.2.3-59-g8ed1b From c155f6499f9797f200aa46eb3ccbf198f4206970 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 4 Feb 2019 17:25:52 +0100 Subject: PM-runtime: Switch accounting over to ktime_get_mono_fast_ns() Similar to what happened whith autosuspend, a deadlock has been reported with PM-runtime accounting in the call path: change_clocksource ... write_seqcount_begin ... timekeeping_update ... sh_cmt_clocksource_enable ... rpm_resume update_pm_runtime_accounting ktime_get do read_seqcount_begin while read_seqcount_retry .... write_seqcount_end Make PM-runtime accounting use ktime_get_mono_fast_ns() to avoid this problem. With ktime_get_mono_fast_ns(), the timestamp is not guaranteed to be monotonic across an update of timekeeping and as a result time can go backward. Add a test to skip accounting for such situation which should stay exceptional. Fixes: a08c2a5a3194 ("PM-runtime: Replace jiffies-based accounting with ktime-based accounting") Reported-by: Biju Das Signed-off-by: Vincent Guittot Reviewed-by: Ulf Hansson [ rjw: Subject, changelog, comment cleanup ] Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 1caa3944898f..6a58bb77a018 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -66,13 +66,22 @@ static int rpm_suspend(struct device *dev, int rpmflags); */ void update_pm_runtime_accounting(struct device *dev) { - u64 now = ktime_to_ns(ktime_get()); + u64 now = ktime_get_mono_fast_ns(); + u64 last = dev->power.accounting_timestamp; u64 delta; - delta = now - dev->power.accounting_timestamp; - dev->power.accounting_timestamp = now; + /* + * Because ktime_get_mono_fast_ns() is not monotonic during + * timekeeping updates, ensure that 'now' is after the last saved + * timesptamp. + */ + if (now < last) + return; + + delta = now - last; + if (dev->power.disable_depth > 0) return; @@ -1312,7 +1321,7 @@ void pm_runtime_enable(struct device *dev) /* About to enable runtime pm, set accounting_timestamp to now */ if (!dev->power.disable_depth) - dev->power.accounting_timestamp = ktime_to_ns(ktime_get()); + dev->power.accounting_timestamp = ktime_get_mono_fast_ns(); } else { dev_warn(dev, "Unbalanced %s!\n", __func__); } -- cgit v1.2.3-59-g8ed1b From fed7e88c0702e30fcd55563ca6eecd548a5d13af Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 4 Feb 2019 17:25:53 +0100 Subject: PM-runtime: update time accounting only when enabled Update the accounting_timestamp field only when PM runtime is enabled and don't forget to account the last state before disabling it. Suggested-by: Ulf Hansson Signed-off-by: Vincent Guittot Reviewed-by: Ulf Hansson [ rjw: Minor cleanups ] Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 6a58bb77a018..04407d9f76fd 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -66,10 +66,14 @@ static int rpm_suspend(struct device *dev, int rpmflags); */ void update_pm_runtime_accounting(struct device *dev) { - u64 now = ktime_get_mono_fast_ns(); - u64 last = dev->power.accounting_timestamp; - u64 delta; + u64 now, last, delta; + if (dev->power.disable_depth > 0) + return; + + last = dev->power.accounting_timestamp; + + now = ktime_get_mono_fast_ns(); dev->power.accounting_timestamp = now; /* @@ -82,9 +86,6 @@ void update_pm_runtime_accounting(struct device *dev) delta = now - last; - if (dev->power.disable_depth > 0) - return; - if (dev->power.runtime_status == RPM_SUSPENDED) dev->power.suspended_time += delta; else @@ -1298,6 +1299,9 @@ void __pm_runtime_disable(struct device *dev, bool check_resume) pm_runtime_put_noidle(dev); } + /* Update time accounting before disabling PM-runtime. */ + update_pm_runtime_accounting(dev); + if (!dev->power.disable_depth++) __pm_runtime_barrier(dev); @@ -1521,7 +1525,6 @@ void pm_runtime_init(struct device *dev) dev->power.request_pending = false; dev->power.request = RPM_REQ_NONE; dev->power.deferred_resume = false; - dev->power.accounting_timestamp = 0; INIT_WORK(&dev->power.work, pm_runtime_work); dev->power.timer_expires = 0; -- cgit v1.2.3-59-g8ed1b From 4a0fa9f9fdb5a7230da6fb250c270c01d5f21fb5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 Feb 2019 13:17:12 +0100 Subject: PM / suspend: Print debug messages for device using direct-complete Devices using the direct-complete optimization are not present it debug messages printed by the core device suspend and resume code, which sometimes makes it difficult to diagnose problems related to them, so add debug messages for those devices. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 0992e67e862b..337a56ff11b7 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1741,8 +1741,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (dev->power.direct_complete) { if (pm_runtime_status_suspended(dev)) { pm_runtime_disable(dev); - if (pm_runtime_status_suspended(dev)) + if (pm_runtime_status_suspended(dev)) { + pm_dev_dbg(dev, state, "direct-complete "); goto Complete; + } pm_runtime_enable(dev); } -- cgit v1.2.3-59-g8ed1b From 85945c28b5a888043cb2b54f880d80d8915f21f5 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 14 Feb 2019 18:29:10 +0000 Subject: PM / core: Add support to skip power management in device/driver model All device objects in the driver model contain fields that control the handling of various power management activities. However, it's not always useful. There are few instances where pseudo devices are added to the model just to take advantage of many other features like kobjects, udev events, and so on. One such example is cpu devices and their caches. The sysfs for the cpu caches are managed by adding devices with cpu as the parent in cpu_device_create() when secondary cpu is brought online. Generally when the secondary CPUs are hotplugged back in as part of resume from suspend-to-ram, we call cpu_device_create() from the cpu hotplug state machine while the cpu device associated with that CPU is not yet ready to be resumed as the device_resume() call happens bit later. It's not really needed to set the flag is_prepared for cpu devices as they are mostly pseudo device and hotplug framework deals with state machine and not managed through the cpu device. This often results in annoying warning when resuming: Enabling non-boot CPUs ... CPU1: Booted secondary processor cache: parent cpu1 should not be sleeping CPU1 is up CPU2: Booted secondary processor cache: parent cpu2 should not be sleeping CPU2 is up .... and so on. So in order to fix these kind of errors, we could just completely avoid doing any power management related initialisations and operations if they are not used by these devices. Add no_pm flags to indicate that the device doesn't require any sort of PM activities and all of them can be completely skipped. We can use the same flag to also avoid adding not used *power* sysfs entries for these devices. For now, lets use this for cpu cache devices. Reviewed-by: Ulf Hansson Signed-off-by: Sudeep Holla Tested-by: Eugeniu Rosca Signed-off-by: Rafael J. Wysocki --- drivers/base/cpu.c | 1 + drivers/base/power/main.c | 7 +++++++ drivers/base/power/sysfs.c | 6 ++++++ include/linux/device.h | 10 ++++++++++ include/linux/pm.h | 1 + 5 files changed, 25 insertions(+) (limited to 'drivers/base/power') diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index eb9443d5bae1..6ce93a52bf3f 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -427,6 +427,7 @@ __cpu_device_create(struct device *parent, void *drvdata, dev->parent = parent; dev->groups = groups; dev->release = device_create_release; + device_set_pm_not_required(dev); dev_set_drvdata(dev, drvdata); retval = kobject_set_name_vargs(&dev->kobj, fmt, args); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 337a56ff11b7..893ae464bfd6 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -124,6 +124,10 @@ void device_pm_unlock(void) */ void device_pm_add(struct device *dev) { + /* Skip PM setup/initialization. */ + if (device_pm_not_required(dev)) + return; + pr_debug("PM: Adding info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); device_pm_check_callbacks(dev); @@ -142,6 +146,9 @@ void device_pm_add(struct device *dev) */ void device_pm_remove(struct device *dev) { + if (device_pm_not_required(dev)) + return; + pr_debug("PM: Removing info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); complete_all(&dev->power.completion); diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 96c8a227610a..c6bf76124184 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -653,6 +653,10 @@ int dpm_sysfs_add(struct device *dev) { int rc; + /* No need to create PM sysfs if explicitly disabled. */ + if (device_pm_not_required(dev)) + return 0; + rc = sysfs_create_group(&dev->kobj, &pm_attr_group); if (rc) return rc; @@ -732,6 +736,8 @@ void rpm_sysfs_remove(struct device *dev) void dpm_sysfs_remove(struct device *dev) { + if (device_pm_not_required(dev)) + return; sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_tolerance_attr_group); dev_pm_qos_constraints_destroy(dev); rpm_sysfs_remove(dev); diff --git a/include/linux/device.h b/include/linux/device.h index 6cb4640b6160..53028636fe39 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1165,6 +1165,16 @@ static inline bool device_async_suspend_enabled(struct device *dev) return !!dev->power.async_suspend; } +static inline bool device_pm_not_required(struct device *dev) +{ + return dev->power.no_pm; +} + +static inline void device_set_pm_not_required(struct device *dev) +{ + dev->power.no_pm = true; +} + static inline void dev_pm_syscore_device(struct device *dev, bool val) { #ifdef CONFIG_PM_SLEEP diff --git a/include/linux/pm.h b/include/linux/pm.h index 3d2cbf947768..06f7ed893928 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -592,6 +592,7 @@ struct dev_pm_info { bool is_suspended:1; /* Ditto */ bool is_noirq_suspended:1; bool is_late_suspended:1; + bool no_pm:1; bool early_init:1; /* Owned by the PM core */ bool direct_complete:1; /* Owned by the PM core */ u32 driver_flags; -- cgit v1.2.3-59-g8ed1b