From patchwork Fri Jun 17 06:26:54 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 9182621 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 453D46075F for ; Fri, 17 Jun 2016 06:29:27 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 33EDE1FF0B for ; Fri, 17 Jun 2016 06:29:27 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 28C5D27EED; Fri, 17 Jun 2016 06:29:27 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8049D2094F for ; Fri, 17 Jun 2016 06:29:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755247AbcFQG1S (ORCPT ); Fri, 17 Jun 2016 02:27:18 -0400 Received: from mailout1.w1.samsung.com ([210.118.77.11]:32224 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755198AbcFQG1Q (ORCPT ); Fri, 17 Jun 2016 02:27:16 -0400 Received: from eucpsbgm1.samsung.com (unknown [203.254.199.244]) by mailout1.w1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0O8W0067JKLCR090@mailout1.w1.samsung.com>; Fri, 17 Jun 2016 07:27:13 +0100 (BST) X-AuditID: cbfec7f4-f796c6d000001486-c0-576398404283 Received: from eusync4.samsung.com ( [203.254.199.214]) by eucpsbgm1.samsung.com (EUCPMTA) with SMTP id 7C.83.05254.04893675; Fri, 17 Jun 2016 07:27:12 +0100 (BST) Received: from AMDC2765.digital.local ([106.116.147.25]) by eusync4.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0O8W00HUZKL21910@eusync4.samsung.com>; Fri, 17 Jun 2016 07:27:12 +0100 (BST) From: Marek Szyprowski To: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, iommu@lists.linux-foundation.org, linux-samsung-soc@vger.kernel.org, linux-arm-kernel@lists.infradead.org Cc: Marek Szyprowski , Joerg Roedel , Inki Dae , Kukjin Kim , Krzysztof Kozlowski , Bartlomiej Zolnierkiewicz , "Rafael J. Wysocki" , Ulf Hansson , Mark Brown , Greg Kroah-Hartman , "Rafael J. Wysocki" Subject: [PATCH v2 04/10] PM core: Make runtime PM of devices use device links Date: Fri, 17 Jun 2016 08:26:54 +0200 Message-id: <1466144820-6286-5-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1466144820-6286-1-git-send-email-m.szyprowski@samsung.com> References: <1466144820-6286-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrOLMWRmVeSWpSXmKPExsVy+t/xa7oOM5LDDX581bfYOGM9q8XUh0/Y LJoXr2ezmHR/AovFgv3WFp2zN7BbvH5haNH/+DWzxabH11gtLu+aw2bxufcIo8WM8/uYLNYe uctu8XjFW3aLM6cvsVocXxvuIODx5OA8Jo/Fe14yeWxa1cnmcefaHjaP/XPXsHtsXlLvMfnG ckaPLVfbWTz6tqxi9Pi8SS6AK4rLJiU1J7MstUjfLoEro+PUcraCG84Vd6ZxNjA+Muti5OSQ EDCRWNU8kw3CFpO4cG89kM3FISSwlFFiwdYj7BBOE5PE24MHGUGq2AQMJbredoFViQisZJTY tmcfM4jDLHCVWWLOnp0sIFXCAn4SH+/9YAKxWQRUJWbunMUKYvMKuEtM3ruZCWKfnMTJY5PB 4pwCHhIvbv0A6xUCqtl1+BTzBEbeBYwMqxhFU0uTC4qT0nMN9YoTc4tL89L1kvNzNzFCgvvL DsbFx6wOMQpwMCrx8K4QTQ4XYk0sK67MPcQowcGsJMKrOR0oxJuSWFmVWpQfX1Sak1p8iFGa g0VJnHfurvchQgLpiSWp2ampBalFMFkmDk6pBkb3zo5fzAujFpX/vHzFo3eRsG1DWcCSPTYc 2zaVLv30Ic4pivvfodqk4KglTG3VVg4Gk44s2xa4d/u1hyvmc9w4XMSz/cme3P4Y/Yz0QiY2 2yfur1dNZZ5VKaflwqG+bsmbK8JmFwt0r6X8SvQ4763nX/h+5ZYHUjb/WbJOxmxm4Lq9pYxP OEqJpTgj0VCLuag4EQBrzlmDagIAAA== Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: "Rafael J. Wysocki" Modify the runtime PM framework to use device links to ensure that supplier devices will not be suspended if any of their consumer devices are active. The idea is to reference count suppliers on the consumer's resume and drop references to them on its suspend. The information on whether or not the supplier has been reference counted by the consumer's (runtime) resume is stored in a new field (rpm_active) in the link object for each link. It may be necessary to clean up those references when the supplier is unbinding and that's why the links whose status is DEVICE_LINK_SUPPLIER_UNBIND are skipped by the runtime suspend and resume code. The above means that if the consumer device is probed in the runtime-active state, the supplier has to be resumed and reference counted by device_link_add() so the code works as expected on its (runtime) suspend. There is a new flag, DEVICE_LINK_RPM_ACTIVE, to tell device_link_add() about that (in which case the caller is responsible for making sure that the consumer really will be runtime-active when runtime PM is enabled for it). The other new link flag, DEVICE_LINK_PM_RUNTIME, tells the core whether or not the link should be used for runtime PM at all. Signed-off-by: Rafael J. Wysocki Signed-off-by: Marek Szyprowski --- drivers/base/core.c | 15 +++++++ drivers/base/dd.c | 1 + drivers/base/power/runtime.c | 93 +++++++++++++++++++++++++++++++++++++++++--- include/linux/device.h | 5 +++ include/linux/pm_runtime.h | 2 + 5 files changed, 111 insertions(+), 5 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 51a479ed68b5..1fe7cf232e1e 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -88,6 +88,11 @@ struct devlink *device_link_add(struct device *consumer, if (!consumer || !supplier || !flags) return NULL; +#define RPM_ACTIVE_FLAGS (DEVICE_LINK_PM_RUNTIME | DEVICE_LINK_PROBE_TIME) + if ((flags & DEVICE_LINK_RPM_ACTIVE) + && (flags & RPM_ACTIVE_FLAGS) != RPM_ACTIVE_FLAGS) + return NULL; + mutex_lock(&device_links_lock); list_for_each_entry(link, &supplier->supplier_links, s_node) @@ -98,6 +103,16 @@ struct devlink *device_link_add(struct device *consumer, if (!link) goto out; + if (flags & DEVICE_LINK_RPM_ACTIVE) { + if (pm_runtime_get_sync(supplier) < 0) { + pm_runtime_put_noidle(supplier); + kfree(link); + goto out; + } + link->rpm_active = true; + } else { + link->rpm_active = false; + } get_device(supplier); link->supplier = supplier; INIT_LIST_HEAD(&link->s_node); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 7c0abeba89e9..2efec98ddb12 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -791,6 +791,7 @@ static void __device_release_driver(struct device *dev, struct device *parent) } pm_runtime_get_sync(dev); + pm_runtime_clean_up_links(dev); driver_sysfs_remove(dev); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index b74690418504..ba21c123b16e 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -12,6 +12,8 @@ #include #include #include + +#include "../base.h" #include "power.h" typedef int (*pm_callback_t)(struct device *); @@ -266,19 +268,69 @@ static int rpm_check_suspend_allowed(struct device *dev) static int __rpm_callback(int (*cb)(struct device *), struct device *dev) __releases(&dev->power.lock) __acquires(&dev->power.lock) { - int retval; + struct devlink *link; + int retval, idx; - if (dev->power.irq_safe) + if (dev->power.irq_safe) { spin_unlock(&dev->power.lock); - else + } else { spin_unlock_irq(&dev->power.lock); + /* + * Resume suppliers if necessary. + * + * The device's runtime PM status cannot change until this + * routine returns, so it is safe to read the status outside of + * the lock. + */ + if (dev->power.runtime_status == RPM_RESUMING) { + idx = device_links_read_lock(); + + list_for_each_entry_rcu(link, &dev->consumer_links, c_node) + if ((link->flags & DEVICE_LINK_PM_RUNTIME) + && link->status != DEVICE_LINK_SUPPLIER_UNBIND + && !link->rpm_active) { + retval = pm_runtime_get_sync(link->supplier); + if (retval < 0) { + pm_runtime_put_noidle(link->supplier); + goto fail; + } + link->rpm_active = true; + } + + device_links_read_unlock(idx); + } + } + retval = cb(dev); - if (dev->power.irq_safe) + if (dev->power.irq_safe) { spin_lock(&dev->power.lock); - else + } else { + /* + * If the device is suspending and the callback has returned + * success, drop the usage counters of the suppliers that have + * been reference counted on its resume. + * + * Do that if resume fails too. + */ + if ((dev->power.runtime_status == RPM_SUSPENDING && !retval) + || (dev->power.runtime_status == RPM_RESUMING && retval)) { + idx = device_links_read_lock(); + + fail: + list_for_each_entry_rcu(link, &dev->consumer_links, c_node) + if (link->status != DEVICE_LINK_SUPPLIER_UNBIND + && link->rpm_active) { + pm_runtime_put(link->supplier); + link->rpm_active = false; + } + + device_links_read_unlock(idx); + } + spin_lock_irq(&dev->power.lock); + } return retval; } @@ -1443,6 +1495,37 @@ void pm_runtime_remove(struct device *dev) } /** + * pm_runtime_clean_up_links - Prepare links to consumers for driver removal. + * @dev: Device whose driver is going to be removed. + * + * Check links from this device to any consumers and if any of them have active + * runtime PM references to the device, drop the usage counter of the device + * (once per link). + * + * Since the device is guaranteed to be runtime-active at the point this is + * called, nothing else needs to be done here. + * + * Moreover, this is called after device_links_busy() has returned 'false', so + * the status of each link is guaranteed to be DEVICE_LINK_SUPPLIER_UNBIND and + * therefore rpm_active can't be manipulated concurrently. + */ +void pm_runtime_clean_up_links(struct device *dev) +{ + struct devlink *link; + int idx; + + idx = device_links_read_lock(); + + list_for_each_entry_rcu(link, &dev->supplier_links, s_node) + if (link->rpm_active) { + pm_runtime_put_noidle(dev); + link->rpm_active = false; + } + + device_links_read_unlock(idx); +} + +/** * pm_runtime_force_suspend - Force a device into suspend state if needed. * @dev: Device to suspend. * diff --git a/include/linux/device.h b/include/linux/device.h index 647204bd74a0..536ce4058e50 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -719,9 +719,13 @@ enum devlink_status { * * PERSISTENT: Do not delete the link on consumer device driver unbind. * PROBE_TIME: Assume supplier device functional when creating the link. + * PM_RUNTIME: If set, the runtime PM framework will use this link. + * RPM_ACTIVE: Run pm_runtime_get_sync() on the supplier during link creation. */ #define DEVICE_LINK_PERSISTENT (1 << 0) #define DEVICE_LINK_PROBE_TIME (1 << 1) +#define DEVICE_LINK_PM_RUNTIME (1 << 2) +#define DEVICE_LINK_RPM_ACTIVE (1 << 3) struct devlink { struct device *supplier; @@ -730,6 +734,7 @@ struct devlink { struct list_head c_node; enum devlink_status status; u32 flags; + bool rpm_active; spinlock_t lock; struct rcu_head rcu_head; }; diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 2e14d2667b6c..c21a21c064e3 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -55,6 +55,7 @@ extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); extern void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns); extern void pm_runtime_set_memalloc_noio(struct device *dev, bool enable); +extern void pm_runtime_clean_up_links(struct device *dev); static inline void pm_suspend_ignore_children(struct device *dev, bool enable) { @@ -186,6 +187,7 @@ static inline unsigned long pm_runtime_autosuspend_expiration( struct device *dev) { return 0; } static inline void pm_runtime_set_memalloc_noio(struct device *dev, bool enable){} +static inline void pm_runtime_clean_up_links(struct device *dev) {} #endif /* !CONFIG_PM */