Received: by 2002:a25:ad19:0:0:0:0:0 with SMTP id y25csp2049858ybi; Mon, 1 Jul 2019 05:19:41 -0700 (PDT) X-Google-Smtp-Source: APXvYqzjhmdPNM8RYcAx96QkhB4gl34U80Th4L4qQK1WR+tvRy+yl+yjsap97uNgGq8timM7oQ87 X-Received: by 2002:a65:48c3:: with SMTP id o3mr21858843pgs.70.1561983581357; Mon, 01 Jul 2019 05:19:41 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1561983581; cv=none; d=google.com; s=arc-20160816; b=tavWKM0J5B8TgA+f4bNLHz+kF0S2M60xGCW4nGuqeSu2fQVTqp/yviK2m3WAExZ8Se wXFMF7LCW6FBCigSVX16obiHj79fUsGt1ktlg5CKS3JHaJyCEKnWfOdYEWtJP9EZArU8 osXvUwngUXABbrPfXKU0y0R2dCKPsLfibXp2bffo2aB1gDyVhpeAGVgAnspVF+bN4q0i U76izd5j3/mZNgxt9tpuahbw1M2Oj3FVoMcEXwAio2Lglba2z456I1uf0KOP/j9Rvj52 iJMIgp0pYeWLrpftRykM+MukOF+vSNR6j3xdTXG2Vb+Lo0Whu7TperfQJ52ShIq1of+h 5UMA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=aj7xqsRW8Kufb5uqu9gWjv9SzgMhRMf7lG1g58ijltE=; b=m6f26W3W0+Lq6rF+F1DUyGpMPeuPE8yqUGHgEgqNoBGySsadGzBBdv/bzqJLTTKWH8 qj9WJ1I9Hxr8HJTBAUP+xBKiJagHMeCaR9dgcKBAZw2mOS+WDpTl9I/z/MsNmqDxNcX8 5ief4as/l8mvvvzHYN08pFeDCf20Oplk6QyJ3g8MT3Rg2haIBefYpwXEPbOcvFqMlP16 TpQj6XUgwaJahPNJV4reI9hZk0HswlZL1Au6yq3TikBVhzCmMdGfK2IZLwP2nO9RyYWQ vwTopLrgRohMH0dveMBY/SXkgD1jHl31RIW1LW+Axz4fOglEB8MqUBfvGr6IfDvAURXd 8jbQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id m14si10018500pgj.377.2019.07.01.05.19.20; Mon, 01 Jul 2019 05:19:41 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-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-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728206AbfGAK6d (ORCPT + 99 others); Mon, 1 Jul 2019 06:58:33 -0400 Received: from cloudserver094114.home.pl ([79.96.170.134]:43078 "EHLO cloudserver094114.home.pl" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727714AbfGAK6U (ORCPT ); Mon, 1 Jul 2019 06:58:20 -0400 Received: from 79.184.254.216.ipv4.supernova.orange.pl (79.184.254.216) (HELO kreacher.localnet) by serwer1319399.home.pl (79.96.170.134) with SMTP (IdeaSmtpServer 0.83.267) id f1d95cac40fb2e78; Mon, 1 Jul 2019 12:58:17 +0200 From: "Rafael J. Wysocki" To: Linux PM Cc: Linux PCI , Linux ACPI , LKML , Bjorn Helgaas , Andy Shevchenko , Mika Westerberg , Hans De Goede , "Robert R. Howell" Subject: [PATCH v2 4/5] ACPI: PM: Introduce "poweroff" callbacks for ACPI PM domain and LPSS Date: Mon, 01 Jul 2019 12:54:29 +0200 Message-ID: <4053873.pF5Dq2SYBR@kreacher> In-Reply-To: <4976412.ihyb9sT5jY@kreacher> References: <4976412.ihyb9sT5jY@kreacher> MIME-Version: 1.0 Content-Transfer-Encoding: 7Bit Content-Type: text/plain; charset="us-ascii" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Rafael J. Wysocki In general, it is not correct to call pm_generic_suspend(), pm_generic_suspend_late() and pm_generic_suspend_noirq() during the hibernation's "poweroff" transition, because device drivers may provide special callbacks to be invoked then and the wrappers in question cause system suspend callbacks to be run. Unfortunately, that happens in the ACPI PM domain and ACPI LPSS. To address this potential issue, introduce "poweroff" callbacks for the ACPI PM and LPSS that will use pm_generic_poweroff(), pm_generic_poweroff_late() and pm_generic_poweroff_noirq() as appropriate. Fixes: 05087360fd7a (ACPI / PM: Take SMART_SUSPEND driver flag into account) Signed-off-by: Rafael J. Wysocki --- -> v2: * Make the new "poweroff" callbacks for the LPSS follow the current acpi_lpss_suspend_late/noirq() behavior. --- drivers/acpi/acpi_lpss.c | 50 ++++++++++++++++++++++++++++++++++++++-- drivers/acpi/device_pm.c | 58 ++++++++++++++++++++++++++++++++++++++++++++--- include/linux/acpi.h | 2 + 3 files changed, 104 insertions(+), 6 deletions(-) Index: linux-pm/drivers/acpi/acpi_lpss.c =================================================================== --- linux-pm.orig/drivers/acpi/acpi_lpss.c +++ linux-pm/drivers/acpi/acpi_lpss.c @@ -1061,6 +1061,13 @@ static int acpi_lpss_suspend_noirq(struc int ret; if (pdata->dev_desc->resume_from_noirq) { + /* + * The driver's ->suspend_late callback will be invoked by + * acpi_lpss_do_suspend_late(), with the assumption that the + * driver really wanted to run that code in ->suspend_noirq, but + * it could not run after acpi_dev_suspend() and the driver + * expected the latter to be called in the "late" phase. + */ ret = acpi_lpss_do_suspend_late(dev); if (ret) return ret; @@ -1147,6 +1154,43 @@ static int acpi_lpss_restore_noirq(struc /* This is analogous to what happens in acpi_lpss_resume_noirq(). */ return acpi_lpss_do_restore_early(dev); } + +static int acpi_lpss_do_poweroff_late(struct device *dev) +{ + int ret = pm_generic_poweroff_late(dev); + + return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev)); +} + +static int acpi_lpss_poweroff_late(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + if (pdata->dev_desc->resume_from_noirq) + return 0; + + return acpi_lpss_do_poweroff_late(dev); +} + +static int acpi_lpss_poweroff_noirq(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + if (pdata->dev_desc->resume_from_noirq) { + /* This is analogous to the acpi_lpss_suspend_noirq() case. */ + int ret = acpi_lpss_do_poweroff_late(dev); + if (ret) + return ret; + } + + return pm_generic_poweroff_noirq(dev); +} #endif /* CONFIG_PM_SLEEP */ static int acpi_lpss_runtime_suspend(struct device *dev) @@ -1180,9 +1224,9 @@ static struct dev_pm_domain acpi_lpss_pm .resume_noirq = acpi_lpss_resume_noirq, .resume_early = acpi_lpss_resume_early, .freeze = acpi_subsys_freeze, - .poweroff = acpi_subsys_suspend, - .poweroff_late = acpi_lpss_suspend_late, - .poweroff_noirq = acpi_lpss_suspend_noirq, + .poweroff = acpi_subsys_poweroff, + .poweroff_late = acpi_lpss_poweroff_late, + .poweroff_noirq = acpi_lpss_poweroff_noirq, .restore_noirq = acpi_lpss_restore_noirq, .restore_early = acpi_lpss_restore_early, #endif Index: linux-pm/drivers/acpi/device_pm.c =================================================================== --- linux-pm.orig/drivers/acpi/device_pm.c +++ linux-pm/drivers/acpi/device_pm.c @@ -1176,6 +1176,58 @@ int acpi_subsys_restore_early(struct dev return ret ? ret : pm_generic_restore_early(dev); } EXPORT_SYMBOL_GPL(acpi_subsys_restore_early); + +/** + * acpi_subsys_poweroff - Run the device driver's poweroff callback. + * @dev: Device to handle. + * + * Follow PCI and resume devices from runtime suspend before running their + * system poweroff callbacks, unless the driver can cope with runtime-suspended + * devices during system suspend and there are no ACPI-specific reasons for + * resuming them. + */ +int acpi_subsys_poweroff(struct device *dev) +{ + if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) || + acpi_dev_needs_resume(dev, ACPI_COMPANION(dev))) + pm_runtime_resume(dev); + + return pm_generic_poweroff(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_poweroff); + +/** + * acpi_subsys_poweroff_late - Run the device driver's poweroff callback. + * @dev: Device to handle. + * + * Carry out the generic late poweroff procedure for @dev and use ACPI to put + * it into a low-power state during system transition into a sleep state. + */ +static int acpi_subsys_poweroff_late(struct device *dev) +{ + int ret; + + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + ret = pm_generic_poweroff_late(dev); + if (ret) + return ret; + + return acpi_dev_suspend(dev, device_may_wakeup(dev)); +} + +/** + * acpi_subsys_poweroff_noirq - Run the driver's "noirq" poweroff callback. + * @dev: Device to suspend. + */ +static int acpi_subsys_poweroff_noirq(struct device *dev) +{ + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + return pm_generic_poweroff_noirq(dev); +} #endif /* CONFIG_PM_SLEEP */ static struct dev_pm_domain acpi_general_pm_domain = { @@ -1191,9 +1243,9 @@ static struct dev_pm_domain acpi_general .resume_noirq = acpi_subsys_resume_noirq, .resume_early = acpi_subsys_resume_early, .freeze = acpi_subsys_freeze, - .poweroff = acpi_subsys_suspend, - .poweroff_late = acpi_subsys_suspend_late, - .poweroff_noirq = acpi_subsys_suspend_noirq, + .poweroff = acpi_subsys_poweroff, + .poweroff_late = acpi_subsys_poweroff_late, + .poweroff_noirq = acpi_subsys_poweroff_noirq, .restore_early = acpi_subsys_restore_early, #endif }, Index: linux-pm/include/linux/acpi.h =================================================================== --- linux-pm.orig/include/linux/acpi.h +++ linux-pm/include/linux/acpi.h @@ -920,6 +920,7 @@ int acpi_subsys_suspend_late(struct devi int acpi_subsys_suspend_noirq(struct device *dev); int acpi_subsys_suspend(struct device *dev); int acpi_subsys_freeze(struct device *dev); +int acpi_subsys_poweroff(struct device *dev); #else static inline int acpi_dev_resume_early(struct device *dev) { return 0; } static inline int acpi_subsys_prepare(struct device *dev) { return 0; } @@ -928,6 +929,7 @@ static inline int acpi_subsys_suspend_la static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; } static inline int acpi_subsys_suspend(struct device *dev) { return 0; } static inline int acpi_subsys_freeze(struct device *dev) { return 0; } +static inline int acpi_subsys_poweroff(struct device *dev) { return 0; } #endif #ifdef CONFIG_ACPI