From patchwork Wed Nov 28 08:17:28 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vincent Guittot X-Patchwork-Id: 152236 Delivered-To: patch@linaro.org Received: by 2002:a2e:299d:0:0:0:0:0 with SMTP id p29-v6csp802707ljp; Wed, 28 Nov 2018 00:17:42 -0800 (PST) X-Google-Smtp-Source: AFSGD/V/JFqc+t9fz4dl6xQYCe+aT5zbGQ7f3P0YGwr/LFpQcq1Z7lVa8fNUdxsqZxAJxHgXEZhY X-Received: by 2002:a63:f30d:: with SMTP id l13mr32583458pgh.399.1543393061882; Wed, 28 Nov 2018 00:17:41 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1543393061; cv=none; d=google.com; s=arc-20160816; b=qjrAWIIVdPuutBVK4Ic5uzU0aDnmMo+L2SYCYK/tRN4RiMzrGxGTH9iPbOa2x/vo8M FOkvFihlMl1iF4z/SjmUBzVuftrkIfm+8epe5vYIh3eIxQ7lKY/aKvQfjRp1vuCCbWRg JgQO+gWHm4j58Ap8DP6wplN3UlXSGUgf6a7ZbPA57TrW5fn8MVeDsQOG7KddB7MCxEXD GoCfscLXZKeymZlaonrtzrwcnWDs9Jn97cS9ZPQoOxgG5teaIrZr5dEe8FA7N2e/C+Y/ 0WTKq/iyAMIfswla+WqSd8hSDKr1ujJzoQbjwBirOzRiMUBxaL2dJMIgCXD/z60S0w/b BuNg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:message-id:date:subject:cc:to:from :dkim-signature; bh=5ae0lMbhgOWN+39l1+LparkQ9mWyzZXpV1MLwHO3P6E=; b=u1js1l3dgtkEaExqyVb6lA6LhIueyg0ZGfJcaZFdlzPt2o7qc85mj6OQ/Vq+4ad8/G 4fZB/FsOEeGlRzeSFfGS550lwtfu89VUlrZFEDAug2roD7eylzqZpqCi/4UIiEFfBGqH uLKQ6ZHbQGe4oPcGUaiEq/4tpxGjBnvLhKK+d6gRGY9WCjYuDwpT3ahCy++WwTqcKHAM +ZV71Zte1k9yx5hodD9UbLVCd67CC/M2y+c7rjFHRDbaaD2GaexECiCjNx9fspY3irpy nczO7BWYgQIGPYsApP0ZAmDTV/dbq66XG+sRjfVHzcp8V+Kf/fNsDkIvgdLMfAH3lO0X AeXA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=BQXCQ644; 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; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id j14si6290042pgi.354.2018.11.28.00.17.41; Wed, 28 Nov 2018 00:17:41 -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; dkim=pass header.i=@linaro.org header.s=google header.b=BQXCQ644; 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; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727341AbeK1TS0 (ORCPT + 11 others); Wed, 28 Nov 2018 14:18:26 -0500 Received: from mail-wm1-f68.google.com ([209.85.128.68]:33086 "EHLO mail-wm1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727268AbeK1TS0 (ORCPT ); Wed, 28 Nov 2018 14:18:26 -0500 Received: by mail-wm1-f68.google.com with SMTP id l3so1792838wme.0 for ; Wed, 28 Nov 2018 00:17:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=5ae0lMbhgOWN+39l1+LparkQ9mWyzZXpV1MLwHO3P6E=; b=BQXCQ644iPUmJVawzt5F35qrvosCN+PaYs35O9pWlmiMWQ0ML2nv5b+Gy1a53HVDAS +In8sI2cAMMLIAU+G3GkNP12MSPgQeSnZI3EpjKmTNY1WgqQjJ4J9m+sdHBZtowHoAiM Sc9MhlHezamlviCHA6nvQyn2mmLBUffYSXAkA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=5ae0lMbhgOWN+39l1+LparkQ9mWyzZXpV1MLwHO3P6E=; b=Eck5UGf91HGPiNOW2XU/olRcVkK4FaaMMvs5pmBfkvF6MzVPI+qHvfyx5obms7zK7R cPsWSBU8dvYIUpt/ey3iqeSK72UCEn1kITEyb88dgzU9whaAkAHF7ccDcCyNLw5pfp+M +RVPeDByAUdxU8U2ZTNM9xGZraf/2E342Di6wLci7/EynJvvaJV6s8hahG4+Dtj9nMm0 +AuLG8iPaD4Wqv4p+G0XjmZRgyj2pZvDNybbE8EPpjAS0FA4KADYOsoHblhEvDbHi2aT WGk3MrRJumaQc5WLfvybQQkyHRMLA9frI63Gbi1rKZ392uicgnyQqT3BK0mFH6VP8rEL X7yg== X-Gm-Message-State: AA+aEWaf9OtxV2y94quaGaCw2SHNT2ZfyYaIX49aBzrbfBqBaP/hDcuf 9Qo5QF5MTmwdM/rWXtZXsevEIYy7ZKI= X-Received: by 2002:a1c:1d4f:: with SMTP id d76mr910882wmd.98.1543393056216; Wed, 28 Nov 2018 00:17:36 -0800 (PST) Received: from localhost.localdomain ([2a01:e0a:f:6020:f0bd:579c:2cc5:83b1]) by smtp.gmail.com with ESMTPSA id j33sm7267092wre.91.2018.11.28.00.17.34 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 28 Nov 2018 00:17:35 -0800 (PST) From: Vincent Guittot To: linux-pm@vger.kernel.org, rjw@rjwysocki.net Cc: ulf.hansson@linaro.org, Vincent Guittot Subject: [PATCH] pm_runtime: move autosuspend on hrtimer Date: Wed, 28 Nov 2018 09:17:28 +0100 Message-Id: <1543393048-30426-1-git-send-email-vincent.guittot@linaro.org> X-Mailer: git-send-email 2.7.4 Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org pm runtime uses the timer infrastructure for autosuspend. This implies that the minimum time before autosuspending a device is in the range [1 tick - 2 ticks [ -On arm64 this means [4ms - 8ms[ with default jiffies configuration -And on arm, it is [10ms - 20ms[ These values are quite high for embedded system which sometimes wants duration in the range of 1 ms. We can move autosuspend on hrtimer to get finer granularity for short duration and takes advantage of slack to keep some margins and gathers long timeout in minimum wakeups. On an arm64 platform that uses 1ms of its GPU, the power consumption improves by 10% for idle use case with hrtimer. The performance impact on arm64 hikey octo coresis : - mark_last_busy: from 1.11 us to 1.25 us - rpm_suspend: from 15.54 us to 15.38 us Only the code path of rpm_suspend that starts hrtimer has been measured arm64 image (arm64 default defconfig) decreases by around 3KB with following details: $ size vmlinux-timer text data bss dec hex filename 12034646 6869268 386840 19290754 1265a82 vmlinux $ size vmlinux-hrtimer text data bss dec hex filename 12030550 6870164 387032 19287746 1264ec2 vmlinux The performance impact on arm 32bits snowball dual cores is : - mark_last_busy: from 0.31 us usec to 0.77 us - rpm_suspend: from 6.83 us to 6.67 usec The increase of the image for snowball platform that I used for testing performance impact, is neglictable (244B) $ size vmlinux-timer text data bss dec hex filename 7157961 2119580 264120 9541661 91981d build-ux500/vmlinux size vmlinux-hrtimer text data bss dec hex filename 7157773 2119884 264248 9541905 919911 vmlinux-hrtimer And arm 32bits image (multi_v7_defconfig) increases by around 1.7KB with following details: $ size vmlinux-timer text data bss dec hex filename 13304443 6803420 402768 20510631 138f7a7 vmlinux $ size vmlinux-hrtimer text data bss dec hex filename 13304299 6805276 402768 20512343 138fe57 vmlinux Signed-off-by: Vincent Guittot --- drivers/base/power/runtime.c | 63 ++++++++++++++++++++++++-------------------- include/linux/pm.h | 5 ++-- include/linux/pm_runtime.h | 6 ++--- 3 files changed, 40 insertions(+), 34 deletions(-) -- 2.7.4 diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index beb85c3..7062469 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -8,6 +8,8 @@ */ #include +#include +#include #include #include #include @@ -93,7 +95,7 @@ static void __update_runtime_status(struct device *dev, enum rpm_status status) static void pm_runtime_deactivate_timer(struct device *dev) { if (dev->power.timer_expires > 0) { - del_timer(&dev->power.suspend_timer); + hrtimer_cancel(&dev->power.suspend_timer); dev->power.timer_expires = 0; } } @@ -124,12 +126,11 @@ static void pm_runtime_cancel_pending(struct device *dev) * This function may be called either with or without dev->power.lock held. * Either way it can be racy, since power.last_busy may be updated at any time. */ -unsigned long pm_runtime_autosuspend_expiration(struct device *dev) +u64 pm_runtime_autosuspend_expiration(struct device *dev) { int autosuspend_delay; - long elapsed; - unsigned long last_busy; - unsigned long expires = 0; + u64 last_busy, expires = 0; + u64 now = ktime_to_ns(ktime_get()); if (!dev->power.use_autosuspend) goto out; @@ -139,19 +140,9 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev) goto out; last_busy = READ_ONCE(dev->power.last_busy); - elapsed = jiffies - last_busy; - if (elapsed < 0) - goto out; /* jiffies has wrapped around. */ - /* - * If the autosuspend_delay is >= 1 second, align the timer by rounding - * up to the nearest second. - */ - expires = last_busy + msecs_to_jiffies(autosuspend_delay); - if (autosuspend_delay >= 1000) - expires = round_jiffies(expires); - expires += !expires; - if (elapsed >= expires - last_busy) + expires = last_busy + autosuspend_delay * NSEC_PER_MSEC; + if (expires <= now) expires = 0; /* Already expired. */ out: @@ -515,7 +506,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) /* If the autosuspend_delay time hasn't expired yet, reschedule. */ if ((rpmflags & RPM_AUTO) && dev->power.runtime_status != RPM_SUSPENDING) { - unsigned long expires = pm_runtime_autosuspend_expiration(dev); + u64 expires = pm_runtime_autosuspend_expiration(dev); if (expires != 0) { /* Pending requests need to be canceled. */ @@ -528,10 +519,20 @@ static int rpm_suspend(struct device *dev, int rpmflags) * expire; pm_suspend_timer_fn() will take care of the * rest. */ - if (!(dev->power.timer_expires && time_before_eq( - dev->power.timer_expires, expires))) { + if (!(dev->power.timer_expires && + dev->power.timer_expires <= expires)) { + /* + * We add a slack of 25% to gather wakeups + * without sacrificing the granularity. + */ + u64 slack = READ_ONCE(dev->power.autosuspend_delay) * + (NSEC_PER_MSEC >> 2); + dev->power.timer_expires = expires; - mod_timer(&dev->power.suspend_timer, expires); + hrtimer_start_range_ns(&dev->power.suspend_timer, + ns_to_ktime(expires), + slack, + HRTIMER_MODE_ABS); } dev->power.timer_autosuspends = 1; goto out; @@ -895,23 +896,25 @@ static void pm_runtime_work(struct work_struct *work) * * Check if the time is right and queue a suspend request. */ -static void pm_suspend_timer_fn(struct timer_list *t) +static enum hrtimer_restart pm_suspend_timer_fn(struct hrtimer *timer) { - struct device *dev = from_timer(dev, t, power.suspend_timer); + struct device *dev = container_of(timer, struct device, power.suspend_timer); unsigned long flags; - unsigned long expires; + u64 expires; spin_lock_irqsave(&dev->power.lock, flags); expires = dev->power.timer_expires; /* If 'expire' is after 'jiffies' we've been called too early. */ - if (expires > 0 && !time_after(expires, jiffies)) { + if (expires > 0 && expires < ktime_to_ns(ktime_get())) { dev->power.timer_expires = 0; rpm_suspend(dev, dev->power.timer_autosuspends ? (RPM_ASYNC | RPM_AUTO) : RPM_ASYNC); } spin_unlock_irqrestore(&dev->power.lock, flags); + + return HRTIMER_NORESTART; } /** @@ -922,6 +925,7 @@ static void pm_suspend_timer_fn(struct timer_list *t) int pm_schedule_suspend(struct device *dev, unsigned int delay) { unsigned long flags; + ktime_t expires; int retval; spin_lock_irqsave(&dev->power.lock, flags); @@ -938,10 +942,10 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay) /* Other scheduled or pending requests need to be canceled. */ pm_runtime_cancel_pending(dev); - dev->power.timer_expires = jiffies + msecs_to_jiffies(delay); - dev->power.timer_expires += !dev->power.timer_expires; + expires = ktime_add(ktime_get(), ms_to_ktime(delay)); + dev->power.timer_expires = ktime_to_ns(expires); dev->power.timer_autosuspends = 0; - mod_timer(&dev->power.suspend_timer, dev->power.timer_expires); + hrtimer_start(&dev->power.suspend_timer, expires, HRTIMER_MODE_ABS); out: spin_unlock_irqrestore(&dev->power.lock, flags); @@ -1491,7 +1495,8 @@ void pm_runtime_init(struct device *dev) INIT_WORK(&dev->power.work, pm_runtime_work); dev->power.timer_expires = 0; - timer_setup(&dev->power.suspend_timer, pm_suspend_timer_fn, 0); + hrtimer_init(&dev->power.suspend_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + dev->power.suspend_timer.function = pm_suspend_timer_fn; init_waitqueue_head(&dev->power.wait_queue); } diff --git a/include/linux/pm.h b/include/linux/pm.h index e723b78..0bd9de1 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -26,6 +26,7 @@ #include #include #include +#include #include /* @@ -608,7 +609,7 @@ struct dev_pm_info { unsigned int should_wakeup:1; #endif #ifdef CONFIG_PM - struct timer_list suspend_timer; + struct hrtimer suspend_timer; unsigned long timer_expires; struct work_struct work; wait_queue_head_t wait_queue; @@ -631,7 +632,7 @@ struct dev_pm_info { enum rpm_status runtime_status; int runtime_error; int autosuspend_delay; - unsigned long last_busy; + u64 last_busy; unsigned long active_jiffies; unsigned long suspended_jiffies; unsigned long accounting_timestamp; diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index f0fc470..54af4ee 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -51,7 +51,7 @@ extern void pm_runtime_no_callbacks(struct device *dev); extern void pm_runtime_irq_safe(struct device *dev); extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); -extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); +extern u64 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); @@ -105,7 +105,7 @@ static inline bool pm_runtime_callbacks_present(struct device *dev) static inline void pm_runtime_mark_last_busy(struct device *dev) { - WRITE_ONCE(dev->power.last_busy, jiffies); + WRITE_ONCE(dev->power.last_busy, ktime_to_ns(ktime_get())); } static inline bool pm_runtime_is_irq_safe(struct device *dev) @@ -168,7 +168,7 @@ static inline void __pm_runtime_use_autosuspend(struct device *dev, bool use) {} static inline void pm_runtime_set_autosuspend_delay(struct device *dev, int delay) {} -static inline unsigned long pm_runtime_autosuspend_expiration( +static inline u64 pm_runtime_autosuspend_expiration( struct device *dev) { return 0; } static inline void pm_runtime_set_memalloc_noio(struct device *dev, bool enable){}