From patchwork Wed Jan 20 08:00:55 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhaoyang Huang X-Patchwork-Id: 60008 Delivered-To: patch@linaro.org Received: by 10.112.130.2 with SMTP id oa2csp3029368lbb; Wed, 20 Jan 2016 00:02:27 -0800 (PST) X-Received: by 10.66.141.165 with SMTP id rp5mr50923947pab.56.1453276946996; Wed, 20 Jan 2016 00:02:26 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id us7si15316748pac.204.2016.01.20.00.02.26; Wed, 20 Jan 2016 00:02:26 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-pm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-pm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-pm-owner@vger.kernel.org; dkim=neutral (body hash did not verify) header.i=@linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751570AbcATIBh (ORCPT + 11 others); Wed, 20 Jan 2016 03:01:37 -0500 Received: from mail-pf0-f177.google.com ([209.85.192.177]:35626 "EHLO mail-pf0-f177.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751067AbcATIBg (ORCPT ); Wed, 20 Jan 2016 03:01:36 -0500 Received: by mail-pf0-f177.google.com with SMTP id 65so701141pff.2 for ; Wed, 20 Jan 2016 00:01:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id; bh=rPrAk/mDwfo8dFuhcnICZwKAdCOYuLluk6lFQRweH+c=; b=Ql9u3uXR7OUC0h5b96hPZEQ0BD6xflnUh8V5exrDRym1KAkkM9BcEe7DPEK1Fv2mxZ ZnAuife4Tpp14RIHoit2lh4DIVJ/55GpXCpbOJcbGGhizxMOujkH66EdevsxgpMmdFgt g6IIPXUDSQe+HW4/TFFTttaOXz6TB2DZY5G+o= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id; bh=rPrAk/mDwfo8dFuhcnICZwKAdCOYuLluk6lFQRweH+c=; b=Jd6D23imMrcMXId+u+cj7L7siw8n3mFsRn0rrNfHLG8XERdvx+TrzkVbIwiQDulQU0 BJdS8tM6GHWzCRgcsqSOAW+DSb98arzMKNCtMvqXjzUVCoQdr7ewtpxlaLBfA28JgNkv AWatRuBBcdwd2mmBetKLd3xneDDAzo/4uVq8XcaIt+zQJZicec3AQJAUR0sjlwzQgjB4 /nKFvQi1JgT7MkSNHrmThbbnRIngxqsyCUYRuOTG5JhKsJ9PkaEPNPfXvpLhMyMXwSYd tiMWdm/BLuORdhxLBE9z6AP6FUdSeeWzy8AfMFP99BUbd/BR7thk+VsoJlQHSeFFPIdO SY4w== X-Gm-Message-State: ALoCoQnaOqVr1/K3NVntjXhdEK2oPL96x0lZRrASM8t1OHKxhVrrqb0RsgZxzesMl6toXYV65WMp7GTsTO7kEZ/aGCyMN6/Bhg== X-Received: by 10.98.71.197 with SMTP id p66mr51478788pfi.166.1453276896092; Wed, 20 Jan 2016 00:01:36 -0800 (PST) Received: from zyhuangubtpc.spreadtrum.com ([175.111.195.49]) by smtp.gmail.com with ESMTPSA id oj9sm47018126pab.8.2016.01.20.00.01.32 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 20 Jan 2016 00:01:35 -0800 (PST) From: Zhaoyang Huang X-Google-Original-From: Zhaoyang Huang To: zhaoyang.huang@spreadtrum.com, rjw@rjwysocki.net, pavel@ucw.cz, linux-kernel@vger.kernel.org, len.brown@intel.com, gregkh@linuxfoundation.org, linux-pm@vger.kernel.org Subject: [RFC PATCH v2] rpm: refining the rpm_suspend function Date: Wed, 20 Jan 2016 16:00:55 +0800 Message-Id: <1453276855-21403-1-git-send-email-zhaoyang.huang@spreadtrum.com> X-Mailer: git-send-email 1.7.9.5 Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org There are too many branch path within he original rpm_suspend funciton,which make the code a little bit hard for understanding and debugging.Just try to split the function into some small one to eliminate the goto and make one optimization for avoiding transfering from wait to auto when irq_safe flag set. The original rpm_suspend function is divied into bellowing functions, which will transfer to corresponding ones like a state machine. _rpm_suspend_auto _rpm_suspend_wait _rpm_suspend_call _rpm_suspend_success _rpm_suspend_fail --- drivers/base/power/runtime.c | 270 +++++++++++++++++++++++++++--------------- 1 file changed, 173 insertions(+), 97 deletions(-) -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index e1a10a0..bff28c9 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -11,10 +11,18 @@ #include #include #include +#include #include #include "power.h" +struct rpm_suspend_retval; typedef int (*pm_callback_t)(struct device *); +typedef void (*_rpm_suspend_func)(struct device *, int, \ + struct rpm_suspend_retval *); +typedef struct rpm_suspend_retval{ + int retval; + void (*pfun)(struct device *, int, struct rpm_suspend_retval *); +} rpm_susp_rv; static pm_callback_t __rpm_get_callback(struct device *dev, size_t cb_offset) { @@ -49,6 +57,17 @@ static pm_callback_t __rpm_get_callback(struct device *dev, size_t cb_offset) static int rpm_resume(struct device *dev, int rpmflags); static int rpm_suspend(struct device *dev, int rpmflags); +static void _rpm_suspend_auto(struct device *dev, int rpmflags, \ + struct rpm_suspend_retval *prv); +static void _rpm_suspend_wait(struct device *dev, int rpmflags, \ + struct rpm_suspend_retval *prv); +static void _rpm_suspend_call(struct device *dev, int rpmflags, \ + struct rpm_suspend_retval *prv); +static void _rpm_suspend_success(struct device *dev, int rpmflags, \ + struct rpm_suspend_retval *prv); +static void _rpm_suspend_fail(struct device *dev, int rpmflags, \ + struct rpm_suspend_retval *prv); + /** * update_pm_runtime_accounting - Update the time accounting of power states * @dev: Device to update the accounting for @@ -391,52 +410,14 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) return retval != -EACCES ? retval : -EIO; } -/** - * rpm_suspend - Carry out runtime suspend of given device. - * @dev: Device to suspend. - * @rpmflags: Flag bits. - * - * Check if the device's runtime PM status allows it to be suspended. - * Cancel a pending idle notification, autosuspend or suspend. If - * another suspend has been started earlier, either return immediately - * or wait for it to finish, depending on the RPM_NOWAIT and RPM_ASYNC - * flags. If the RPM_ASYNC flag is set then queue a suspend request; - * otherwise run the ->runtime_suspend() callback directly. When - * ->runtime_suspend succeeded, if a deferred resume was requested while - * the callback was running then carry it out, otherwise send an idle - * notification for its parent (if the suspend succeeded and both - * ignore_children of parent->power and irq_safe of dev->power are not set). - * If ->runtime_suspend failed with -EAGAIN or -EBUSY, and if the RPM_AUTO - * flag is set and the next autosuspend-delay expiration time is in the - * future, schedule another autosuspend attempt. - * - * This function must be called under dev->power.lock with interrupts disabled. - */ -static int rpm_suspend(struct device *dev, int rpmflags) - __releases(&dev->power.lock) __acquires(&dev->power.lock) +static void _rpm_suspend_auto(struct device *dev, int rpmflags, + struct rpm_suspend_retval *prv) { - int (*callback)(struct device *); - struct device *parent = NULL; - int retval; - - trace_rpm_suspend(dev, rpmflags); - - repeat: - retval = rpm_check_suspend_allowed(dev); - - if (retval < 0) - ; /* Conditions are wrong. */ - - /* Synchronous suspends are not allowed in the RPM_RESUMING state. */ - else if (dev->power.runtime_status == RPM_RESUMING && - !(rpmflags & RPM_ASYNC)) - retval = -EAGAIN; - if (retval) - goto out; + prv->pfun = _rpm_suspend_wait; /* If the autosuspend_delay time hasn't expired yet, reschedule. */ if ((rpmflags & RPM_AUTO) - && dev->power.runtime_status != RPM_SUSPENDING) { + && dev->power.runtime_status != RPM_SUSPENDING) { unsigned long expires = pm_runtime_autosuspend_expiration(dev); if (expires != 0) { @@ -444,21 +425,29 @@ static int rpm_suspend(struct device *dev, int rpmflags) dev->power.request = RPM_REQ_NONE; /* - * Optimization: If the timer is already running and is - * set to expire at or before the autosuspend delay, - * avoid the overhead of resetting it. Just let it - * expire; pm_suspend_timer_fn() will take care of the - * rest. - */ + * Optimization: If the timer is already running and is + * set to expire at or before the autosuspend delay, + * avoid the overhead of resetting it. Just let it + * expire; pm_suspend_timer_fn() will take care of the + * rest. + */ if (!(dev->power.timer_expires && time_before_eq( - dev->power.timer_expires, expires))) { + dev->power.timer_expires, expires))) { dev->power.timer_expires = expires; mod_timer(&dev->power.suspend_timer, expires); } dev->power.timer_autosuspends = 1; - goto out; + prv->pfun = NULL; } } +} + +static void _rpm_suspend_wait(struct device *dev, int rpmflags, + struct rpm_suspend_retval *prv) +{ + unsigned long expires = 0; + + prv->pfun = _rpm_suspend_call; /* Other scheduled or pending requests need to be canceled. */ pm_runtime_cancel_pending(dev); @@ -467,24 +456,30 @@ static int rpm_suspend(struct device *dev, int rpmflags) DEFINE_WAIT(wait); if (rpmflags & (RPM_ASYNC | RPM_NOWAIT)) { - retval = -EINPROGRESS; - goto out; + prv->retval = -EINPROGRESS; + prv->pfun = NULL; + return; } if (dev->power.irq_safe) { - spin_unlock(&dev->power.lock); + while (dev->power.runtime_status == + RPM_SUSPENDING) { + spin_unlock(&dev->power.lock); - cpu_relax(); + cpu_relax(); - spin_lock(&dev->power.lock); - goto repeat; + spin_lock(&dev->power.lock); + continue; + } } - /* Wait for the other suspend running in parallel with us. */ + /* Wait for the other suspend + running in parallel with us. */ for (;;) { - prepare_to_wait(&dev->power.wait_queue, &wait, - TASK_UNINTERRUPTIBLE); - if (dev->power.runtime_status != RPM_SUSPENDING) + prepare_to_wait(&dev->power.wait_queue, + &wait, TASK_UNINTERRUPTIBLE); + if (dev->power.runtime_status + != RPM_SUSPENDING) break; spin_unlock_irq(&dev->power.lock); @@ -494,35 +489,58 @@ static int rpm_suspend(struct device *dev, int rpmflags) spin_lock_irq(&dev->power.lock); } finish_wait(&dev->power.wait_queue, &wait); - goto repeat; + + /*check expires firstly for auto suspend mode, + if not, just go ahead to the async*/ + expires = pm_runtime_autosuspend_expiration(dev); + if (expires != 0) + prv->pfun = _rpm_suspend_auto; } +} - if (dev->power.no_callbacks) - goto no_callback; /* Assume success. */ +/*call the function async or sync by the callback*/ +static void _rpm_suspend_call(struct device *dev, int rpmflags, + struct rpm_suspend_retval *prv) +{ + pm_callback_t callback; + + prv->pfun = _rpm_suspend_success; + + /*if there is no callbacks, no meaning to place a work into workqueue, + go ahead to check the deferd resume and if the parent can suspend*/ + if (!dev->power.no_callbacks) { + /* Carry out an asynchronous or a synchronous suspend. */ + if (rpmflags & RPM_ASYNC) { + dev->power.request = (rpmflags & RPM_AUTO) ? + RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND; + if (!dev->power.request_pending) { + dev->power.request_pending = true; + queue_work(pm_wq, &dev->power.work); + } + prv->pfun = NULL; + } else { + callback = RPM_GET_CALLBACK(dev, runtime_suspend); - /* Carry out an asynchronous or a synchronous suspend. */ - if (rpmflags & RPM_ASYNC) { - dev->power.request = (rpmflags & RPM_AUTO) ? - RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND; - if (!dev->power.request_pending) { - dev->power.request_pending = true; - queue_work(pm_wq, &dev->power.work); - } - goto out; - } + __update_runtime_status(dev, RPM_SUSPENDING); - __update_runtime_status(dev, RPM_SUSPENDING); + dev_pm_enable_wake_irq(dev); - callback = RPM_GET_CALLBACK(dev, runtime_suspend); + prv->retval = rpm_callback(callback, dev); - dev_pm_enable_wake_irq(dev); - retval = rpm_callback(callback, dev); - if (retval) - goto fail; + if (prv->retval) + prv->pfun = _rpm_suspend_fail; + } + } +} + +static void _rpm_suspend_success(struct device *dev, int rpmflags, + struct rpm_suspend_retval *prv) +{ + struct device *parent; - no_callback: __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_deactivate_timer(dev); + prv->pfun = NULL; if (dev->parent) { parent = dev->parent; @@ -533,8 +551,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) if (dev->power.deferred_resume) { dev->power.deferred_resume = false; rpm_resume(dev, 0); - retval = -EAGAIN; - goto out; + prv->retval = -EAGAIN; } /* Maybe the parent is now able to suspend. */ @@ -547,34 +564,93 @@ static int rpm_suspend(struct device *dev, int rpmflags) spin_lock(&dev->power.lock); } +} - out: - trace_rpm_return_int(dev, _THIS_IP_, retval); - - return retval; - - fail: +static void _rpm_suspend_fail(struct device *dev, int rpmflags, + struct rpm_suspend_retval *prv) +{ dev_pm_disable_wake_irq(dev); __update_runtime_status(dev, RPM_ACTIVE); dev->power.deferred_resume = false; wake_up_all(&dev->power.wait_queue); - if (retval == -EAGAIN || retval == -EBUSY) { + if (prv->retval == -EAGAIN || prv->retval == -EBUSY) { dev->power.runtime_error = 0; /* - * If the callback routine failed an autosuspend, and - * if the last_busy time has been updated so that there - * is a new autosuspend expiration time, automatically - * reschedule another autosuspend. - */ + * If the callback routine failed an autosuspend, and + * if the last_busy time has been updated so that there + * is a new autosuspend expiration time, automatically + * reschedule another autosuspend. + */ if ((rpmflags & RPM_AUTO) && - pm_runtime_autosuspend_expiration(dev) != 0) - goto repeat; + pm_runtime_autosuspend_expiration(dev) != 0) + prv->pfun = _rpm_suspend_auto; } else { pm_runtime_cancel_pending(dev); + prv->pfun = NULL; + } +} + +/** + * rpm_suspend - Carry out runtime suspend of given device. + * @dev: Device to suspend. + * @rpmflags: Flag bits. + * + * Check if the device's runtime PM status allows it to be suspended. + * Cancel a pending idle notification, autosuspend or suspend. If + * another suspend has been started earlier, either return immediately + * or wait for it to finish, depending on the RPM_NOWAIT and RPM_ASYNC + * flags. If the RPM_ASYNC flag is set then queue a suspend request; + * otherwise run the ->runtime_suspend() callback directly. When + * ->runtime_suspend succeeded, if a deferred resume was requested while + * the callback was running then carry it out, otherwise send an idle + * notification for its parent (if the suspend succeeded and both + * ignore_children of parent->power and irq_safe of dev->power are not set). + * If ->runtime_suspend failed with -EAGAIN or -EBUSY, and if the RPM_AUTO + * flag is set and the next autosuspend-delay expiration time is in the + * future, schedule another autosuspend attempt. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +static int rpm_suspend(struct device *dev, int rpmflags) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{ + rpm_susp_rv *prv = (rpm_susp_rv *)malloc(sizeof(struct rpm_suspend_retval)); + _rpm_suspend_func pfun; + int retval; + + if (NULL == prv) + return -ENOMEM; + + trace_rpm_suspend(dev, rpmflags); + + retval = rpm_check_suspend_allowed(dev); + + if (retval < 0) + ; /* Conditions are wrong. */ + + /* Synchronous suspends are not allowed in the RPM_RESUMING state. */ + else if (dev->power.runtime_status == RPM_RESUMING && + !(rpmflags & RPM_ASYNC)) + retval = -EAGAIN; + + if (retval) + ; + else{ + prv->retval = 0; + /*start the process from auto*/ + pfun = _rpm_suspend_auto; + while (pfun) { + pfun(dev, rpmflags, prv); + pfun = prv->pfun; + } + retval = prv->retval; } - goto out; + trace_rpm_return_int(dev, _THIS_IP_, retval); + + free(prv); + return retval; } /**