Commit 7680d45a authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branches 'pm-cpuidle', 'pm-sleep' and 'pm-domains'

Merge cpuidle changes, updates related to system sleep amd generic power
domains code fixes for 6.2-rc1:

 - Improve kernel messages printed by the cpuidle PCI driver (Ulf
   Hansson).

 - Make the DT cpuidle driver return the correct number of parsed idle
   states, clean it up and clarify a comment in it (Ulf Hansson).

 - Modify the tasks freezing code to avoid using pr_cont() and refine an
   error message printed by it (Rafael Wysocki).

 - Make the hibernation core code complain about memory map mismatches
   during resume to help diagnostics (Xueqin Luo).

 - Fix mistake in a kerneldoc comment in the hibernation code (xiongxin).

 - Reverse the order of performance and enabling operations in the
   generic power domains code (Abel Vesa).

 - Power off[on] domains in hibernate .freeze[thaw]_noirq hook of in the
   generic power domains code (Abel Vesa).

 - Consolidate genpd_restore_noirq() and genpd_resume_noirq() (Shawn
   Guo).

 - Pass generic PM noirq hooks to genpd_finish_suspend() (Shawn Guo).

 - Drop generic power domain status manipulation during hibernate
   restore (Shawn Guo).

* pm-cpuidle:
  cpuidle: dt: Clarify a comment and simplify code in dt_init_idle_driver()
  cpuidle: dt: Return the correct numbers of parsed idle states
  cpuidle: psci: Extend information in log about OSI/PC mode

* pm-sleep:
  PM: sleep: Refine error message in try_to_freeze_tasks()
  PM: sleep: Avoid using pr_cont() in the tasks freezing code
  PM: hibernate: Complain about memory map mismatches during resume
  PM: hibernate: Fix mistake in kerneldoc comment

* pm-domains:
  PM: domains: Reverse the order of performance and enabling ops
  PM: domains: Power off[on] domain in hibernate .freeze[thaw]_noirq hook
  PM: domains: Consolidate genpd_restore_noirq() and genpd_resume_noirq()
  PM: domains: Pass generic PM noirq hooks to genpd_finish_suspend()
  PM: domains: Drop genpd status manipulation for hibernate restore
...@@ -939,8 +939,8 @@ static int genpd_runtime_suspend(struct device *dev) ...@@ -939,8 +939,8 @@ static int genpd_runtime_suspend(struct device *dev)
return 0; return 0;
genpd_lock(genpd); genpd_lock(genpd);
gpd_data->rpm_pstate = genpd_drop_performance_state(dev);
genpd_power_off(genpd, true, 0); genpd_power_off(genpd, true, 0);
gpd_data->rpm_pstate = genpd_drop_performance_state(dev);
genpd_unlock(genpd); genpd_unlock(genpd);
return 0; return 0;
...@@ -978,9 +978,8 @@ static int genpd_runtime_resume(struct device *dev) ...@@ -978,9 +978,8 @@ static int genpd_runtime_resume(struct device *dev)
goto out; goto out;
genpd_lock(genpd); genpd_lock(genpd);
genpd_restore_performance_state(dev, gpd_data->rpm_pstate);
ret = genpd_power_on(genpd, 0); ret = genpd_power_on(genpd, 0);
if (!ret)
genpd_restore_performance_state(dev, gpd_data->rpm_pstate);
genpd_unlock(genpd); genpd_unlock(genpd);
if (ret) if (ret)
...@@ -1018,8 +1017,8 @@ static int genpd_runtime_resume(struct device *dev) ...@@ -1018,8 +1017,8 @@ static int genpd_runtime_resume(struct device *dev)
err_poweroff: err_poweroff:
if (!pm_runtime_is_irq_safe(dev) || genpd_is_irq_safe(genpd)) { if (!pm_runtime_is_irq_safe(dev) || genpd_is_irq_safe(genpd)) {
genpd_lock(genpd); genpd_lock(genpd);
gpd_data->rpm_pstate = genpd_drop_performance_state(dev);
genpd_power_off(genpd, true, 0); genpd_power_off(genpd, true, 0);
gpd_data->rpm_pstate = genpd_drop_performance_state(dev);
genpd_unlock(genpd); genpd_unlock(genpd);
} }
...@@ -1189,12 +1188,15 @@ static int genpd_prepare(struct device *dev) ...@@ -1189,12 +1188,15 @@ static int genpd_prepare(struct device *dev)
* genpd_finish_suspend - Completion of suspend or hibernation of device in an * genpd_finish_suspend - Completion of suspend or hibernation of device in an
* I/O pm domain. * I/O pm domain.
* @dev: Device to suspend. * @dev: Device to suspend.
* @poweroff: Specifies if this is a poweroff_noirq or suspend_noirq callback. * @suspend_noirq: Generic suspend_noirq callback.
* @resume_noirq: Generic resume_noirq callback.
* *
* Stop the device and remove power from the domain if all devices in it have * Stop the device and remove power from the domain if all devices in it have
* been stopped. * been stopped.
*/ */
static int genpd_finish_suspend(struct device *dev, bool poweroff) static int genpd_finish_suspend(struct device *dev,
int (*suspend_noirq)(struct device *dev),
int (*resume_noirq)(struct device *dev))
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
int ret = 0; int ret = 0;
...@@ -1203,10 +1205,7 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff) ...@@ -1203,10 +1205,7 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff)
if (IS_ERR(genpd)) if (IS_ERR(genpd))
return -EINVAL; return -EINVAL;
if (poweroff) ret = suspend_noirq(dev);
ret = pm_generic_poweroff_noirq(dev);
else
ret = pm_generic_suspend_noirq(dev);
if (ret) if (ret)
return ret; return ret;
...@@ -1217,10 +1216,7 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff) ...@@ -1217,10 +1216,7 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff)
!pm_runtime_status_suspended(dev)) { !pm_runtime_status_suspended(dev)) {
ret = genpd_stop_dev(genpd, dev); ret = genpd_stop_dev(genpd, dev);
if (ret) { if (ret) {
if (poweroff) resume_noirq(dev);
pm_generic_restore_noirq(dev);
else
pm_generic_resume_noirq(dev);
return ret; return ret;
} }
} }
...@@ -1244,16 +1240,20 @@ static int genpd_suspend_noirq(struct device *dev) ...@@ -1244,16 +1240,20 @@ static int genpd_suspend_noirq(struct device *dev)
{ {
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
return genpd_finish_suspend(dev, false); return genpd_finish_suspend(dev,
pm_generic_suspend_noirq,
pm_generic_resume_noirq);
} }
/** /**
* genpd_resume_noirq - Start of resume of device in an I/O PM domain. * genpd_finish_resume - Completion of resume of device in an I/O PM domain.
* @dev: Device to resume. * @dev: Device to resume.
* @resume_noirq: Generic resume_noirq callback.
* *
* Restore power to the device's PM domain, if necessary, and start the device. * Restore power to the device's PM domain, if necessary, and start the device.
*/ */
static int genpd_resume_noirq(struct device *dev) static int genpd_finish_resume(struct device *dev,
int (*resume_noirq)(struct device *dev))
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
int ret; int ret;
...@@ -1265,7 +1265,7 @@ static int genpd_resume_noirq(struct device *dev) ...@@ -1265,7 +1265,7 @@ static int genpd_resume_noirq(struct device *dev)
return -EINVAL; return -EINVAL;
if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd)) if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
return pm_generic_resume_noirq(dev); return resume_noirq(dev);
genpd_lock(genpd); genpd_lock(genpd);
genpd_sync_power_on(genpd, true, 0); genpd_sync_power_on(genpd, true, 0);
...@@ -1282,6 +1282,19 @@ static int genpd_resume_noirq(struct device *dev) ...@@ -1282,6 +1282,19 @@ static int genpd_resume_noirq(struct device *dev)
return pm_generic_resume_noirq(dev); return pm_generic_resume_noirq(dev);
} }
/**
* genpd_resume_noirq - Start of resume of device in an I/O PM domain.
* @dev: Device to resume.
*
* Restore power to the device's PM domain, if necessary, and start the device.
*/
static int genpd_resume_noirq(struct device *dev)
{
dev_dbg(dev, "%s()\n", __func__);
return genpd_finish_resume(dev, pm_generic_resume_noirq);
}
/** /**
* genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. * genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
* @dev: Device to freeze. * @dev: Device to freeze.
...@@ -1293,24 +1306,11 @@ static int genpd_resume_noirq(struct device *dev) ...@@ -1293,24 +1306,11 @@ static int genpd_resume_noirq(struct device *dev)
*/ */
static int genpd_freeze_noirq(struct device *dev) static int genpd_freeze_noirq(struct device *dev)
{ {
const struct generic_pm_domain *genpd;
int ret = 0;
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
genpd = dev_to_genpd(dev); return genpd_finish_suspend(dev,
if (IS_ERR(genpd)) pm_generic_freeze_noirq,
return -EINVAL; pm_generic_thaw_noirq);
ret = pm_generic_freeze_noirq(dev);
if (ret)
return ret;
if (genpd->dev_ops.stop && genpd->dev_ops.start &&
!pm_runtime_status_suspended(dev))
ret = genpd_stop_dev(genpd, dev);
return ret;
} }
/** /**
...@@ -1322,23 +1322,9 @@ static int genpd_freeze_noirq(struct device *dev) ...@@ -1322,23 +1322,9 @@ static int genpd_freeze_noirq(struct device *dev)
*/ */
static int genpd_thaw_noirq(struct device *dev) static int genpd_thaw_noirq(struct device *dev)
{ {
const struct generic_pm_domain *genpd;
int ret = 0;
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
genpd = dev_to_genpd(dev); return genpd_finish_resume(dev, pm_generic_thaw_noirq);
if (IS_ERR(genpd))
return -EINVAL;
if (genpd->dev_ops.stop && genpd->dev_ops.start &&
!pm_runtime_status_suspended(dev)) {
ret = genpd_start_dev(genpd, dev);
if (ret)
return ret;
}
return pm_generic_thaw_noirq(dev);
} }
/** /**
...@@ -1353,7 +1339,9 @@ static int genpd_poweroff_noirq(struct device *dev) ...@@ -1353,7 +1339,9 @@ static int genpd_poweroff_noirq(struct device *dev)
{ {
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
return genpd_finish_suspend(dev, true); return genpd_finish_suspend(dev,
pm_generic_poweroff_noirq,
pm_generic_restore_noirq);
} }
/** /**
...@@ -1365,40 +1353,9 @@ static int genpd_poweroff_noirq(struct device *dev) ...@@ -1365,40 +1353,9 @@ static int genpd_poweroff_noirq(struct device *dev)
*/ */
static int genpd_restore_noirq(struct device *dev) static int genpd_restore_noirq(struct device *dev)
{ {
struct generic_pm_domain *genpd;
int ret = 0;
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
genpd = dev_to_genpd(dev); return genpd_finish_resume(dev, pm_generic_restore_noirq);
if (IS_ERR(genpd))
return -EINVAL;
/*
* At this point suspended_count == 0 means we are being run for the
* first time for the given domain in the present cycle.
*/
genpd_lock(genpd);
if (genpd->suspended_count++ == 0) {
/*
* The boot kernel might put the domain into arbitrary state,
* so make it appear as powered off to genpd_sync_power_on(),
* so that it tries to power it on in case it was really off.
*/
genpd->status = GENPD_STATE_OFF;
}
genpd_sync_power_on(genpd, true, 0);
genpd_unlock(genpd);
if (genpd->dev_ops.stop && genpd->dev_ops.start &&
!pm_runtime_status_suspended(dev)) {
ret = genpd_start_dev(genpd, dev);
if (ret)
return ret;
}
return pm_generic_restore_noirq(dev);
} }
/** /**
...@@ -2749,17 +2706,6 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, ...@@ -2749,17 +2706,6 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
dev->pm_domain->detach = genpd_dev_pm_detach; dev->pm_domain->detach = genpd_dev_pm_detach;
dev->pm_domain->sync = genpd_dev_pm_sync; dev->pm_domain->sync = genpd_dev_pm_sync;
if (power_on) {
genpd_lock(pd);
ret = genpd_power_on(pd, 0);
genpd_unlock(pd);
}
if (ret) {
genpd_remove_device(pd, dev);
return -EPROBE_DEFER;
}
/* Set the default performance state */ /* Set the default performance state */
pstate = of_get_required_opp_performance_state(dev->of_node, index); pstate = of_get_required_opp_performance_state(dev->of_node, index);
if (pstate < 0 && pstate != -ENODEV && pstate != -EOPNOTSUPP) { if (pstate < 0 && pstate != -ENODEV && pstate != -EOPNOTSUPP) {
...@@ -2771,6 +2717,24 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, ...@@ -2771,6 +2717,24 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
goto err; goto err;
dev_gpd_data(dev)->default_pstate = pstate; dev_gpd_data(dev)->default_pstate = pstate;
} }
if (power_on) {
genpd_lock(pd);
ret = genpd_power_on(pd, 0);
genpd_unlock(pd);
}
if (ret) {
/* Drop the default performance state */
if (dev_gpd_data(dev)->default_pstate) {
dev_pm_genpd_set_performance_state(dev, 0);
dev_gpd_data(dev)->default_pstate = 0;
}
genpd_remove_device(pd, dev);
return -EPROBE_DEFER;
}
return 1; return 1;
err: err:
......
...@@ -181,7 +181,8 @@ static int psci_cpuidle_domain_probe(struct platform_device *pdev) ...@@ -181,7 +181,8 @@ static int psci_cpuidle_domain_probe(struct platform_device *pdev)
if (ret) if (ret)
goto remove_pd; goto remove_pd;
pr_info("Initialized CPU PM domain topology\n"); pr_info("Initialized CPU PM domain topology using %s mode\n",
use_osi ? "OSI" : "PC");
return 0; return 0;
put_node: put_node:
......
...@@ -211,18 +211,15 @@ int dt_init_idle_driver(struct cpuidle_driver *drv, ...@@ -211,18 +211,15 @@ int dt_init_idle_driver(struct cpuidle_driver *drv,
of_node_put(cpu_node); of_node_put(cpu_node);
if (err) if (err)
return err; return err;
/*
* Update the driver state count only if some valid DT idle states /* Set the number of total supported idle states. */
* were detected drv->state_count = state_idx;
*/
if (i)
drv->state_count = state_idx;
/* /*
* Return the number of present and valid DT idle states, which can * Return the number of present and valid DT idle states, which can
* also be 0 on platforms with missing DT idle states or legacy DT * also be 0 on platforms with missing DT idle states or legacy DT
* configuration predating the DT idle states bindings. * configuration predating the DT idle states bindings.
*/ */
return i; return state_idx - start_idx;
} }
EXPORT_SYMBOL_GPL(dt_init_idle_driver); EXPORT_SYMBOL_GPL(dt_init_idle_driver);
...@@ -27,6 +27,8 @@ unsigned int __read_mostly freeze_timeout_msecs = 20 * MSEC_PER_SEC; ...@@ -27,6 +27,8 @@ unsigned int __read_mostly freeze_timeout_msecs = 20 * MSEC_PER_SEC;
static int try_to_freeze_tasks(bool user_only) static int try_to_freeze_tasks(bool user_only)
{ {
const char *what = user_only ? "user space processes" :
"remaining freezable tasks";
struct task_struct *g, *p; struct task_struct *g, *p;
unsigned long end_time; unsigned long end_time;
unsigned int todo; unsigned int todo;
...@@ -36,6 +38,8 @@ static int try_to_freeze_tasks(bool user_only) ...@@ -36,6 +38,8 @@ static int try_to_freeze_tasks(bool user_only)
bool wakeup = false; bool wakeup = false;
int sleep_usecs = USEC_PER_MSEC; int sleep_usecs = USEC_PER_MSEC;
pr_info("Freezing %s\n", what);
start = ktime_get_boottime(); start = ktime_get_boottime();
end_time = jiffies + msecs_to_jiffies(freeze_timeout_msecs); end_time = jiffies + msecs_to_jiffies(freeze_timeout_msecs);
...@@ -82,9 +86,8 @@ static int try_to_freeze_tasks(bool user_only) ...@@ -82,9 +86,8 @@ static int try_to_freeze_tasks(bool user_only)
elapsed_msecs = ktime_to_ms(elapsed); elapsed_msecs = ktime_to_ms(elapsed);
if (todo) { if (todo) {
pr_cont("\n"); pr_err("Freezing %s %s after %d.%03d seconds "
pr_err("Freezing of tasks %s after %d.%03d seconds " "(%d tasks refusing to freeze, wq_busy=%d):\n", what,
"(%d tasks refusing to freeze, wq_busy=%d):\n",
wakeup ? "aborted" : "failed", wakeup ? "aborted" : "failed",
elapsed_msecs / 1000, elapsed_msecs % 1000, elapsed_msecs / 1000, elapsed_msecs % 1000,
todo - wq_busy, wq_busy); todo - wq_busy, wq_busy);
...@@ -101,8 +104,8 @@ static int try_to_freeze_tasks(bool user_only) ...@@ -101,8 +104,8 @@ static int try_to_freeze_tasks(bool user_only)
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
} }
} else { } else {
pr_cont("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000, pr_info("Freezing %s completed (elapsed %d.%03d seconds)\n",
elapsed_msecs % 1000); what, elapsed_msecs / 1000, elapsed_msecs % 1000);
} }
return todo ? -EBUSY : 0; return todo ? -EBUSY : 0;
...@@ -130,14 +133,11 @@ int freeze_processes(void) ...@@ -130,14 +133,11 @@ int freeze_processes(void)
static_branch_inc(&freezer_active); static_branch_inc(&freezer_active);
pm_wakeup_clear(0); pm_wakeup_clear(0);
pr_info("Freezing user space processes ... ");
pm_freezing = true; pm_freezing = true;
error = try_to_freeze_tasks(true); error = try_to_freeze_tasks(true);
if (!error) { if (!error)
__usermodehelper_set_disable_depth(UMH_DISABLED); __usermodehelper_set_disable_depth(UMH_DISABLED);
pr_cont("done.");
}
pr_cont("\n");
BUG_ON(in_atomic()); BUG_ON(in_atomic());
/* /*
...@@ -166,14 +166,9 @@ int freeze_kernel_threads(void) ...@@ -166,14 +166,9 @@ int freeze_kernel_threads(void)
{ {
int error; int error;
pr_info("Freezing remaining freezable tasks ... ");
pm_nosig_freezing = true; pm_nosig_freezing = true;
error = try_to_freeze_tasks(false); error = try_to_freeze_tasks(false);
if (!error)
pr_cont("done.");
pr_cont("\n");
BUG_ON(in_atomic()); BUG_ON(in_atomic());
if (error) if (error)
......
...@@ -1723,8 +1723,8 @@ static unsigned long minimum_image_size(unsigned long saveable) ...@@ -1723,8 +1723,8 @@ static unsigned long minimum_image_size(unsigned long saveable)
* /sys/power/reserved_size, respectively). To make this happen, we compute the * /sys/power/reserved_size, respectively). To make this happen, we compute the
* total number of available page frames and allocate at least * total number of available page frames and allocate at least
* *
* ([page frames total] + PAGES_FOR_IO + [metadata pages]) / 2 * ([page frames total] - PAGES_FOR_IO - [metadata pages]) / 2
* + 2 * DIV_ROUND_UP(reserved_size, PAGE_SIZE) * - 2 * DIV_ROUND_UP(reserved_size, PAGE_SIZE)
* *
* of them, which corresponds to the maximum size of a hibernation image. * of them, which corresponds to the maximum size of a hibernation image.
* *
...@@ -2259,10 +2259,14 @@ static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm) ...@@ -2259,10 +2259,14 @@ static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm)
if (unlikely(buf[j] == BM_END_OF_MAP)) if (unlikely(buf[j] == BM_END_OF_MAP))
break; break;
if (pfn_valid(buf[j]) && memory_bm_pfn_present(bm, buf[j])) if (pfn_valid(buf[j]) && memory_bm_pfn_present(bm, buf[j])) {
memory_bm_set_bit(bm, buf[j]); memory_bm_set_bit(bm, buf[j]);
else } else {
if (!pfn_valid(buf[j]))
pr_err(FW_BUG "Memory map mismatch at 0x%llx after hibernation\n",
(unsigned long long)PFN_PHYS(buf[j]));
return -EFAULT; return -EFAULT;
}
} }
return 0; return 0;
......
...@@ -1462,7 +1462,7 @@ class Data: ...@@ -1462,7 +1462,7 @@ class Data:
'TIMEOUT' : r'(?i).*\bTIMEOUT\b.*', 'TIMEOUT' : r'(?i).*\bTIMEOUT\b.*',
'ABORT' : r'(?i).*\bABORT\b.*', 'ABORT' : r'(?i).*\bABORT\b.*',
'IRQ' : r'.*\bgenirq: .*', 'IRQ' : r'.*\bgenirq: .*',
'TASKFAIL': r'.*Freezing of tasks *.*', 'TASKFAIL': r'.*Freezing .*after *.*',
'ACPI' : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*', 'ACPI' : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
'DISKFULL': r'.*\bNo space left on device.*', 'DISKFULL': r'.*\bNo space left on device.*',
'USBERR' : r'.*usb .*device .*, error [0-9-]*', 'USBERR' : r'.*usb .*device .*, error [0-9-]*',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment