Commit d3f2c402 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branches 'pm-core', 'pm-sleep', 'pm-opp' and 'pm-tools'

Merge PM core changes, updates related to system sleep support,
operating performance points (OPP) changes and power management
utilities changes for 6.4-rc1:

 - Drop unnecessary (void *) conversions from the PM core (Li zeming).

 - Add sysfs files to represent time spent in a platform sleep state
   during suspend-to-idle and make AMD and Intel PMC drivers use them
   (Mario Limonciello).

 - Use of_property_present() for testing DT property presence (Rob
   Herring).

 - Add set_required_opps() callback to the 'struct opp_table', to make
   the code paths cleaner (Viresh Kumar).

 - Update the pm-graph siute of utilities to v5.11 with the following
   changes:
   * New script which allows users to install the latest pm-graph
     from the upstream github repo.
   * Update all the dmesg suspend/resume PM print formats to be able to
     process recent timelines using dmesg only.
   * Add ethtool output to the log for the system's ethernet device if
     ethtool exists.
   * Make the tool more robustly handle events where mangled dmesg or
     ftrace outputs do not include all the requisite data.

 - Make the sleepgraph utility recognize "CPU killed" messages (Xueqin
   Luo).

* pm-core:
  PM: core: Remove unnecessary (void *) conversions

* pm-sleep:
  platform/x86/intel/pmc: core: Report duration of time in HW sleep state
  platform/x86/intel/pmc: core: Always capture counters on suspend
  platform/x86/amd: pmc: Report duration of time in hw sleep state
  PM: Add sysfs files to represent time spent in hardware sleep state

* pm-opp:
  OPP: Move required opps configuration to specialized callback
  OPP: Handle all genpd cases together in _set_required_opps()
  opp: Use of_property_present() for testing DT property presence

* pm-tools:
  PM: tools: sleepgraph: Recognize "CPU killed" messages
  pm-graph: Update to v5.11
...@@ -413,6 +413,35 @@ Description: ...@@ -413,6 +413,35 @@ Description:
The /sys/power/suspend_stats/last_failed_step file contains The /sys/power/suspend_stats/last_failed_step file contains
the last failed step in the suspend/resume path. the last failed step in the suspend/resume path.
What: /sys/power/suspend_stats/last_hw_sleep
Date: June 2023
Contact: Mario Limonciello <mario.limonciello@amd.com>
Description:
The /sys/power/suspend_stats/last_hw_sleep file
contains the duration of time spent in a hardware sleep
state in the most recent system suspend-resume cycle.
This number is measured in microseconds.
What: /sys/power/suspend_stats/total_hw_sleep
Date: June 2023
Contact: Mario Limonciello <mario.limonciello@amd.com>
Description:
The /sys/power/suspend_stats/total_hw_sleep file
contains the aggregate of time spent in a hardware sleep
state since the kernel was booted. This number
is measured in microseconds.
What: /sys/power/suspend_stats/max_hw_sleep
Date: June 2023
Contact: Mario Limonciello <mario.limonciello@amd.com>
Description:
The /sys/power/suspend_stats/max_hw_sleep file
contains the maximum amount of time that the hardware can
report for time spent in a hardware sleep state. When sleep
cycles are longer than this time, the values for
'total_hw_sleep' and 'last_hw_sleep' may not be accurate.
This number is measured in microseconds.
What: /sys/power/sync_on_suspend What: /sys/power/sync_on_suspend
Date: October 2019 Date: October 2019
Contact: Jonas Meurer <jonas@freesources.org> Contact: Jonas Meurer <jonas@freesources.org>
......
...@@ -679,7 +679,7 @@ static bool dpm_async_fn(struct device *dev, async_func_t func) ...@@ -679,7 +679,7 @@ static bool dpm_async_fn(struct device *dev, async_func_t func)
static void async_resume_noirq(void *data, async_cookie_t cookie) static void async_resume_noirq(void *data, async_cookie_t cookie)
{ {
struct device *dev = (struct device *)data; struct device *dev = data;
int error; int error;
error = device_resume_noirq(dev, pm_transition, true); error = device_resume_noirq(dev, pm_transition, true);
...@@ -816,7 +816,7 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn ...@@ -816,7 +816,7 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn
static void async_resume_early(void *data, async_cookie_t cookie) static void async_resume_early(void *data, async_cookie_t cookie)
{ {
struct device *dev = (struct device *)data; struct device *dev = data;
int error; int error;
error = device_resume_early(dev, pm_transition, true); error = device_resume_early(dev, pm_transition, true);
...@@ -980,7 +980,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) ...@@ -980,7 +980,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
static void async_resume(void *data, async_cookie_t cookie) static void async_resume(void *data, async_cookie_t cookie)
{ {
struct device *dev = (struct device *)data; struct device *dev = data;
int error; int error;
error = device_resume(dev, pm_transition, true); error = device_resume(dev, pm_transition, true);
...@@ -1269,7 +1269,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a ...@@ -1269,7 +1269,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
static void async_suspend_noirq(void *data, async_cookie_t cookie) static void async_suspend_noirq(void *data, async_cookie_t cookie)
{ {
struct device *dev = (struct device *)data; struct device *dev = data;
int error; int error;
error = __device_suspend_noirq(dev, pm_transition, true); error = __device_suspend_noirq(dev, pm_transition, true);
...@@ -1450,7 +1450,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as ...@@ -1450,7 +1450,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
static void async_suspend_late(void *data, async_cookie_t cookie) static void async_suspend_late(void *data, async_cookie_t cookie)
{ {
struct device *dev = (struct device *)data; struct device *dev = data;
int error; int error;
error = __device_suspend_late(dev, pm_transition, true); error = __device_suspend_late(dev, pm_transition, true);
...@@ -1727,7 +1727,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) ...@@ -1727,7 +1727,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
static void async_suspend(void *data, async_cookie_t cookie) static void async_suspend(void *data, async_cookie_t cookie)
{ {
struct device *dev = (struct device *)data; struct device *dev = data;
int error; int error;
error = __device_suspend(dev, pm_transition, true); error = __device_suspend(dev, pm_transition, true);
......
...@@ -935,8 +935,8 @@ static int _set_opp_bw(const struct opp_table *opp_table, ...@@ -935,8 +935,8 @@ static int _set_opp_bw(const struct opp_table *opp_table,
return 0; return 0;
} }
static int _set_required_opp(struct device *dev, struct device *pd_dev, static int _set_performance_state(struct device *dev, struct device *pd_dev,
struct dev_pm_opp *opp, int i) struct dev_pm_opp *opp, int i)
{ {
unsigned int pstate = likely(opp) ? opp->required_opps[i]->pstate : 0; unsigned int pstate = likely(opp) ? opp->required_opps[i]->pstate : 0;
int ret; int ret;
...@@ -953,37 +953,19 @@ static int _set_required_opp(struct device *dev, struct device *pd_dev, ...@@ -953,37 +953,19 @@ static int _set_required_opp(struct device *dev, struct device *pd_dev,
return ret; return ret;
} }
/* This is only called for PM domain for now */ static int _opp_set_required_opps_generic(struct device *dev,
static int _set_required_opps(struct device *dev, struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down)
struct opp_table *opp_table,
struct dev_pm_opp *opp, bool up)
{ {
struct opp_table **required_opp_tables = opp_table->required_opp_tables; dev_err(dev, "setting required-opps isn't supported for non-genpd devices\n");
struct device **genpd_virt_devs = opp_table->genpd_virt_devs; return -ENOENT;
int i, ret = 0; }
if (!required_opp_tables)
return 0;
/* required-opps not fully initialized yet */
if (lazy_linking_pending(opp_table))
return -EBUSY;
/*
* We only support genpd's OPPs in the "required-opps" for now, as we
* don't know much about other use cases. Error out if the required OPP
* doesn't belong to a genpd.
*/
if (unlikely(!required_opp_tables[0]->is_genpd)) {
dev_err(dev, "required-opps don't belong to a genpd\n");
return -ENOENT;
}
/* Single genpd case */
if (!genpd_virt_devs)
return _set_required_opp(dev, dev, opp, 0);
/* Multiple genpd case */ static int _opp_set_required_opps_genpd(struct device *dev,
struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down)
{
struct device **genpd_virt_devs =
opp_table->genpd_virt_devs ? opp_table->genpd_virt_devs : &dev;
int i, ret = 0;
/* /*
* Acquire genpd_virt_dev_lock to make sure we don't use a genpd_dev * Acquire genpd_virt_dev_lock to make sure we don't use a genpd_dev
...@@ -992,15 +974,15 @@ static int _set_required_opps(struct device *dev, ...@@ -992,15 +974,15 @@ static int _set_required_opps(struct device *dev,
mutex_lock(&opp_table->genpd_virt_dev_lock); mutex_lock(&opp_table->genpd_virt_dev_lock);
/* Scaling up? Set required OPPs in normal order, else reverse */ /* Scaling up? Set required OPPs in normal order, else reverse */
if (up) { if (!scaling_down) {
for (i = 0; i < opp_table->required_opp_count; i++) { for (i = 0; i < opp_table->required_opp_count; i++) {
ret = _set_required_opp(dev, genpd_virt_devs[i], opp, i); ret = _set_performance_state(dev, genpd_virt_devs[i], opp, i);
if (ret) if (ret)
break; break;
} }
} else { } else {
for (i = opp_table->required_opp_count - 1; i >= 0; i--) { for (i = opp_table->required_opp_count - 1; i >= 0; i--) {
ret = _set_required_opp(dev, genpd_virt_devs[i], opp, i); ret = _set_performance_state(dev, genpd_virt_devs[i], opp, i);
if (ret) if (ret)
break; break;
} }
...@@ -1011,6 +993,34 @@ static int _set_required_opps(struct device *dev, ...@@ -1011,6 +993,34 @@ static int _set_required_opps(struct device *dev,
return ret; return ret;
} }
/* This is only called for PM domain for now */
static int _set_required_opps(struct device *dev, struct opp_table *opp_table,
struct dev_pm_opp *opp, bool up)
{
/* required-opps not fully initialized yet */
if (lazy_linking_pending(opp_table))
return -EBUSY;
if (opp_table->set_required_opps)
return opp_table->set_required_opps(dev, opp_table, opp, up);
return 0;
}
/* Update set_required_opps handler */
void _update_set_required_opps(struct opp_table *opp_table)
{
/* Already set */
if (opp_table->set_required_opps)
return;
/* All required OPPs will belong to genpd or none */
if (opp_table->required_opp_tables[0]->is_genpd)
opp_table->set_required_opps = _opp_set_required_opps_genpd;
else
opp_table->set_required_opps = _opp_set_required_opps_generic;
}
static void _find_current_opp(struct device *dev, struct opp_table *opp_table) static void _find_current_opp(struct device *dev, struct opp_table *opp_table)
{ {
struct dev_pm_opp *opp = ERR_PTR(-ENODEV); struct dev_pm_opp *opp = ERR_PTR(-ENODEV);
......
...@@ -196,6 +196,8 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table, ...@@ -196,6 +196,8 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
/* Let's do the linking later on */ /* Let's do the linking later on */
if (lazy) if (lazy)
list_add(&opp_table->lazy, &lazy_opp_tables); list_add(&opp_table->lazy, &lazy_opp_tables);
else
_update_set_required_opps(opp_table);
goto put_np; goto put_np;
...@@ -224,7 +226,7 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, ...@@ -224,7 +226,7 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
of_property_read_u32(np, "voltage-tolerance", of_property_read_u32(np, "voltage-tolerance",
&opp_table->voltage_tolerance_v1); &opp_table->voltage_tolerance_v1);
if (of_find_property(np, "#power-domain-cells", NULL)) if (of_property_present(np, "#power-domain-cells"))
opp_table->is_genpd = true; opp_table->is_genpd = true;
/* Get OPP table node */ /* Get OPP table node */
...@@ -411,6 +413,7 @@ static void lazy_link_required_opp_table(struct opp_table *new_table) ...@@ -411,6 +413,7 @@ static void lazy_link_required_opp_table(struct opp_table *new_table)
/* All required opp-tables found, remove from lazy list */ /* All required opp-tables found, remove from lazy list */
if (!lazy) { if (!lazy) {
_update_set_required_opps(opp_table);
list_del_init(&opp_table->lazy); list_del_init(&opp_table->lazy);
list_for_each_entry(opp, &opp_table->opp_list, node) list_for_each_entry(opp, &opp_table->opp_list, node)
...@@ -536,7 +539,7 @@ static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table, ...@@ -536,7 +539,7 @@ static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
* an OPP then the OPP should not be enabled as there is * an OPP then the OPP should not be enabled as there is
* no way to see if the hardware supports it. * no way to see if the hardware supports it.
*/ */
if (of_find_property(np, "opp-supported-hw", NULL)) if (of_property_present(np, "opp-supported-hw"))
return false; return false;
else else
return true; return true;
......
...@@ -184,6 +184,7 @@ enum opp_table_access { ...@@ -184,6 +184,7 @@ enum opp_table_access {
* @enabled: Set to true if the device's resources are enabled/configured. * @enabled: Set to true if the device's resources are enabled/configured.
* @genpd_performance_state: Device's power domain support performance state. * @genpd_performance_state: Device's power domain support performance state.
* @is_genpd: Marks if the OPP table belongs to a genpd. * @is_genpd: Marks if the OPP table belongs to a genpd.
* @set_required_opps: Helper responsible to set required OPPs.
* @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry: debugfs dentry pointer of the real device directory (not links).
* @dentry_name: Name of the real dentry. * @dentry_name: Name of the real dentry.
* *
...@@ -234,6 +235,8 @@ struct opp_table { ...@@ -234,6 +235,8 @@ struct opp_table {
bool enabled; bool enabled;
bool genpd_performance_state; bool genpd_performance_state;
bool is_genpd; bool is_genpd;
int (*set_required_opps)(struct device *dev,
struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
struct dentry *dentry; struct dentry *dentry;
...@@ -257,6 +260,7 @@ void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cp ...@@ -257,6 +260,7 @@ void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cp
struct opp_table *_add_opp_table_indexed(struct device *dev, int index, bool getclk); struct opp_table *_add_opp_table_indexed(struct device *dev, int index, bool getclk);
void _put_opp_list_kref(struct opp_table *opp_table); void _put_opp_list_kref(struct opp_table *opp_table);
void _required_opps_available(struct dev_pm_opp *opp, int count); void _required_opps_available(struct dev_pm_opp *opp, int count);
void _update_set_required_opps(struct opp_table *opp_table);
static inline bool lazy_linking_pending(struct opp_table *opp_table) static inline bool lazy_linking_pending(struct opp_table *opp_table)
{ {
......
...@@ -393,9 +393,8 @@ static void amd_pmc_validate_deepest(struct amd_pmc_dev *pdev) ...@@ -393,9 +393,8 @@ static void amd_pmc_validate_deepest(struct amd_pmc_dev *pdev)
if (!table.s0i3_last_entry_status) if (!table.s0i3_last_entry_status)
dev_warn(pdev->dev, "Last suspend didn't reach deepest state\n"); dev_warn(pdev->dev, "Last suspend didn't reach deepest state\n");
else pm_report_hw_sleep_time(table.s0i3_last_entry_status ?
dev_dbg(pdev->dev, "Last suspend in deepest state for %lluus\n", table.timein_s0i3_lastcapture : 0);
table.timein_s0i3_lastcapture);
} }
static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev) static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)
...@@ -1015,6 +1014,7 @@ static int amd_pmc_probe(struct platform_device *pdev) ...@@ -1015,6 +1014,7 @@ static int amd_pmc_probe(struct platform_device *pdev)
} }
amd_pmc_dbgfs_register(dev); amd_pmc_dbgfs_register(dev);
pm_report_max_hw_sleep(U64_MAX);
return 0; return 0;
err_pci_dev_put: err_pci_dev_put:
......
...@@ -1153,6 +1153,8 @@ static int pmc_core_probe(struct platform_device *pdev) ...@@ -1153,6 +1153,8 @@ static int pmc_core_probe(struct platform_device *pdev)
pmc_core_do_dmi_quirks(pmcdev); pmc_core_do_dmi_quirks(pmcdev);
pmc_core_dbgfs_register(pmcdev); pmc_core_dbgfs_register(pmcdev);
pm_report_max_hw_sleep(FIELD_MAX(SLP_S0_RES_COUNTER_MASK) *
pmc_core_adjust_slp_s0_step(pmcdev, 1));
device_initialized = true; device_initialized = true;
dev_info(&pdev->dev, " initialized\n"); dev_info(&pdev->dev, " initialized\n");
...@@ -1179,12 +1181,6 @@ static __maybe_unused int pmc_core_suspend(struct device *dev) ...@@ -1179,12 +1181,6 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
{ {
struct pmc_dev *pmcdev = dev_get_drvdata(dev); struct pmc_dev *pmcdev = dev_get_drvdata(dev);
pmcdev->check_counters = false;
/* No warnings on S0ix failures */
if (!warn_on_s0ix_failures)
return 0;
/* Check if the syspend will actually use S0ix */ /* Check if the syspend will actually use S0ix */
if (pm_suspend_via_firmware()) if (pm_suspend_via_firmware())
return 0; return 0;
...@@ -1197,7 +1193,6 @@ static __maybe_unused int pmc_core_suspend(struct device *dev) ...@@ -1197,7 +1193,6 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
if (pmc_core_dev_state_get(pmcdev, &pmcdev->s0ix_counter)) if (pmc_core_dev_state_get(pmcdev, &pmcdev->s0ix_counter))
return -EIO; return -EIO;
pmcdev->check_counters = true;
return 0; return 0;
} }
...@@ -1221,6 +1216,8 @@ static inline bool pmc_core_is_s0ix_failed(struct pmc_dev *pmcdev) ...@@ -1221,6 +1216,8 @@ static inline bool pmc_core_is_s0ix_failed(struct pmc_dev *pmcdev)
if (pmc_core_dev_state_get(pmcdev, &s0ix_counter)) if (pmc_core_dev_state_get(pmcdev, &s0ix_counter))
return false; return false;
pm_report_hw_sleep_time((u32)(s0ix_counter - pmcdev->s0ix_counter));
if (s0ix_counter == pmcdev->s0ix_counter) if (s0ix_counter == pmcdev->s0ix_counter)
return true; return true;
...@@ -1233,12 +1230,16 @@ static __maybe_unused int pmc_core_resume(struct device *dev) ...@@ -1233,12 +1230,16 @@ static __maybe_unused int pmc_core_resume(struct device *dev)
const struct pmc_bit_map **maps = pmcdev->map->lpm_sts; const struct pmc_bit_map **maps = pmcdev->map->lpm_sts;
int offset = pmcdev->map->lpm_status_offset; int offset = pmcdev->map->lpm_status_offset;
if (!pmcdev->check_counters) /* Check if the syspend used S0ix */
if (pm_suspend_via_firmware())
return 0; return 0;
if (!pmc_core_is_s0ix_failed(pmcdev)) if (!pmc_core_is_s0ix_failed(pmcdev))
return 0; return 0;
if (!warn_on_s0ix_failures)
return 0;
if (pmc_core_is_pc10_failed(pmcdev)) { if (pmc_core_is_pc10_failed(pmcdev)) {
/* S0ix failed because of PC10 entry failure */ /* S0ix failed because of PC10 entry failure */
dev_info(dev, "CPU did not enter PC10!!! (PC10 cnt=0x%llx)\n", dev_info(dev, "CPU did not enter PC10!!! (PC10 cnt=0x%llx)\n",
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#define SLP_S0_RES_COUNTER_MASK GENMASK(31, 0)
#define PMC_BASE_ADDR_DEFAULT 0xFE000000 #define PMC_BASE_ADDR_DEFAULT 0xFE000000
/* Sunrise Point Power Management Controller PCI Device ID */ /* Sunrise Point Power Management Controller PCI Device ID */
...@@ -319,7 +321,6 @@ struct pmc_reg_map { ...@@ -319,7 +321,6 @@ struct pmc_reg_map {
* @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers * @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers
* used to read MPHY PG and PLL status are available * used to read MPHY PG and PLL status are available
* @mutex_lock: mutex to complete one transcation * @mutex_lock: mutex to complete one transcation
* @check_counters: On resume, check if counters are getting incremented
* @pc10_counter: PC10 residency counter * @pc10_counter: PC10 residency counter
* @s0ix_counter: S0ix residency (step adjusted) * @s0ix_counter: S0ix residency (step adjusted)
* @num_lpm_modes: Count of enabled modes * @num_lpm_modes: Count of enabled modes
...@@ -338,7 +339,6 @@ struct pmc_dev { ...@@ -338,7 +339,6 @@ struct pmc_dev {
int pmc_xram_read_bit; int pmc_xram_read_bit;
struct mutex lock; /* generic mutex lock for PMC Core */ struct mutex lock; /* generic mutex lock for PMC Core */
bool check_counters; /* Check for counter increments on resume */
u64 pc10_counter; u64 pc10_counter;
u64 s0ix_counter; u64 s0ix_counter;
int num_lpm_modes; int num_lpm_modes;
......
...@@ -68,6 +68,9 @@ struct suspend_stats { ...@@ -68,6 +68,9 @@ struct suspend_stats {
int last_failed_errno; int last_failed_errno;
int errno[REC_FAILED_NUM]; int errno[REC_FAILED_NUM];
int last_failed_step; int last_failed_step;
u64 last_hw_sleep;
u64 total_hw_sleep;
u64 max_hw_sleep;
enum suspend_stat_step failed_steps[REC_FAILED_NUM]; enum suspend_stat_step failed_steps[REC_FAILED_NUM];
}; };
...@@ -489,6 +492,8 @@ void restore_processor_state(void); ...@@ -489,6 +492,8 @@ void restore_processor_state(void);
extern int register_pm_notifier(struct notifier_block *nb); extern int register_pm_notifier(struct notifier_block *nb);
extern int unregister_pm_notifier(struct notifier_block *nb); extern int unregister_pm_notifier(struct notifier_block *nb);
extern void ksys_sync_helper(void); extern void ksys_sync_helper(void);
extern void pm_report_hw_sleep_time(u64 t);
extern void pm_report_max_hw_sleep(u64 t);
#define pm_notifier(fn, pri) { \ #define pm_notifier(fn, pri) { \
static struct notifier_block fn##_nb = \ static struct notifier_block fn##_nb = \
...@@ -526,6 +531,9 @@ static inline int unregister_pm_notifier(struct notifier_block *nb) ...@@ -526,6 +531,9 @@ static inline int unregister_pm_notifier(struct notifier_block *nb)
return 0; return 0;
} }
static inline void pm_report_hw_sleep_time(u64 t) {};
static inline void pm_report_max_hw_sleep(u64 t) {};
static inline void ksys_sync_helper(void) {} static inline void ksys_sync_helper(void) {}
#define pm_notifier(fn, pri) do { (void)(fn); } while (0) #define pm_notifier(fn, pri) do { (void)(fn); } while (0)
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* Copyright (c) 2003 Open Source Development Lab * Copyright (c) 2003 Open Source Development Lab
*/ */
#include <linux/acpi.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/string.h> #include <linux/string.h>
...@@ -83,6 +84,19 @@ int unregister_pm_notifier(struct notifier_block *nb) ...@@ -83,6 +84,19 @@ int unregister_pm_notifier(struct notifier_block *nb)
} }
EXPORT_SYMBOL_GPL(unregister_pm_notifier); EXPORT_SYMBOL_GPL(unregister_pm_notifier);
void pm_report_hw_sleep_time(u64 t)
{
suspend_stats.last_hw_sleep = t;
suspend_stats.total_hw_sleep += t;
}
EXPORT_SYMBOL_GPL(pm_report_hw_sleep_time);
void pm_report_max_hw_sleep(u64 t)
{
suspend_stats.max_hw_sleep = t;
}
EXPORT_SYMBOL_GPL(pm_report_max_hw_sleep);
int pm_notifier_call_chain_robust(unsigned long val_up, unsigned long val_down) int pm_notifier_call_chain_robust(unsigned long val_up, unsigned long val_down)
{ {
int ret; int ret;
...@@ -314,24 +328,27 @@ static char *suspend_step_name(enum suspend_stat_step step) ...@@ -314,24 +328,27 @@ static char *suspend_step_name(enum suspend_stat_step step)
} }
} }
#define suspend_attr(_name) \ #define suspend_attr(_name, format_str) \
static ssize_t _name##_show(struct kobject *kobj, \ static ssize_t _name##_show(struct kobject *kobj, \
struct kobj_attribute *attr, char *buf) \ struct kobj_attribute *attr, char *buf) \
{ \ { \
return sprintf(buf, "%d\n", suspend_stats._name); \ return sprintf(buf, format_str, suspend_stats._name); \
} \ } \
static struct kobj_attribute _name = __ATTR_RO(_name) static struct kobj_attribute _name = __ATTR_RO(_name)
suspend_attr(success); suspend_attr(success, "%d\n");
suspend_attr(fail); suspend_attr(fail, "%d\n");
suspend_attr(failed_freeze); suspend_attr(failed_freeze, "%d\n");
suspend_attr(failed_prepare); suspend_attr(failed_prepare, "%d\n");
suspend_attr(failed_suspend); suspend_attr(failed_suspend, "%d\n");
suspend_attr(failed_suspend_late); suspend_attr(failed_suspend_late, "%d\n");
suspend_attr(failed_suspend_noirq); suspend_attr(failed_suspend_noirq, "%d\n");
suspend_attr(failed_resume); suspend_attr(failed_resume, "%d\n");
suspend_attr(failed_resume_early); suspend_attr(failed_resume_early, "%d\n");
suspend_attr(failed_resume_noirq); suspend_attr(failed_resume_noirq, "%d\n");
suspend_attr(last_hw_sleep, "%llu\n");
suspend_attr(total_hw_sleep, "%llu\n");
suspend_attr(max_hw_sleep, "%llu\n");
static ssize_t last_failed_dev_show(struct kobject *kobj, static ssize_t last_failed_dev_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf) struct kobj_attribute *attr, char *buf)
...@@ -391,12 +408,30 @@ static struct attribute *suspend_attrs[] = { ...@@ -391,12 +408,30 @@ static struct attribute *suspend_attrs[] = {
&last_failed_dev.attr, &last_failed_dev.attr,
&last_failed_errno.attr, &last_failed_errno.attr,
&last_failed_step.attr, &last_failed_step.attr,
&last_hw_sleep.attr,
&total_hw_sleep.attr,
&max_hw_sleep.attr,
NULL, NULL,
}; };
static umode_t suspend_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
{
if (attr != &last_hw_sleep.attr &&
attr != &total_hw_sleep.attr &&
attr != &max_hw_sleep.attr)
return 0444;
#ifdef CONFIG_ACPI
if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)
return 0444;
#endif
return 0;
}
static const struct attribute_group suspend_attr_group = { static const struct attribute_group suspend_attr_group = {
.name = "suspend_stats", .name = "suspend_stats",
.attrs = suspend_attrs, .attrs = suspend_attrs,
.is_visible = suspend_attr_is_visible,
}; };
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
|_| |___/ |_| |_| |___/ |_|
pm-graph: suspend/resume/boot timing analysis tools pm-graph: suspend/resume/boot timing analysis tools
Version: 5.10 Version: 5.11
Author: Todd Brandt <todd.e.brandt@intel.com> Author: Todd Brandt <todd.e.brandt@intel.com>
Home Page: https://www.intel.com/content/www/us/en/developer/topic-technology/open/pm-graph/overview.html Home Page: https://www.intel.com/content/www/us/en/developer/topic-technology/open/pm-graph/overview.html
......
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
#
# Script which clones and installs the latest pm-graph
# from http://github.com/intel/pm-graph.git
OUT=`mktemp -d 2>/dev/null`
if [ -z "$OUT" -o ! -e $OUT ]; then
echo "ERROR: mktemp failed to create folder"
exit
fi
cleanup() {
if [ -e "$OUT" ]; then
cd $OUT
rm -rf pm-graph
cd /tmp
rmdir $OUT
fi
}
git clone http://github.com/intel/pm-graph.git $OUT/pm-graph
if [ ! -e "$OUT/pm-graph/sleepgraph.py" ]; then
echo "ERROR: pm-graph github repo failed to clone"
cleanup
exit
fi
cd $OUT/pm-graph
echo "INSTALLING PM-GRAPH"
sudo make install
if [ $? -eq 0 ]; then
echo "INSTALL SUCCESS"
sleepgraph -v
else
echo "INSTALL FAILED"
fi
cleanup
...@@ -86,7 +86,7 @@ def ascii(text): ...@@ -86,7 +86,7 @@ def ascii(text):
# store system values and test parameters # store system values and test parameters
class SystemValues: class SystemValues:
title = 'SleepGraph' title = 'SleepGraph'
version = '5.10' version = '5.11'
ansi = False ansi = False
rs = 0 rs = 0
display = '' display = ''
...@@ -300,6 +300,7 @@ class SystemValues: ...@@ -300,6 +300,7 @@ class SystemValues:
[0, 'acpidevices', 'sh', '-c', 'ls -l /sys/bus/acpi/devices/*/physical_node'], [0, 'acpidevices', 'sh', '-c', 'ls -l /sys/bus/acpi/devices/*/physical_node'],
[0, 's0ix_require', 'cat', '/sys/kernel/debug/pmc_core/substate_requirements'], [0, 's0ix_require', 'cat', '/sys/kernel/debug/pmc_core/substate_requirements'],
[0, 's0ix_debug', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_debug_status'], [0, 's0ix_debug', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_debug_status'],
[0, 'ethtool', 'ethtool', '{ethdev}'],
[1, 's0ix_residency', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_residency_usec'], [1, 's0ix_residency', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_residency_usec'],
[1, 'interrupts', 'cat', '/proc/interrupts'], [1, 'interrupts', 'cat', '/proc/interrupts'],
[1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'], [1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'],
...@@ -1078,18 +1079,35 @@ class SystemValues: ...@@ -1078,18 +1079,35 @@ class SystemValues:
else: else:
out[data[0].strip()] = data[1] out[data[0].strip()] = data[1]
return out return out
def cmdinfovar(self, arg):
if arg == 'ethdev':
try:
cmd = [self.getExec('ip'), '-4', '-o', '-br', 'addr']
fp = Popen(cmd, stdout=PIPE, stderr=PIPE).stdout
info = ascii(fp.read()).strip()
fp.close()
except:
return 'iptoolcrash'
for line in info.split('\n'):
if line[0] == 'e' and 'UP' in line:
return line.split()[0]
return 'nodevicefound'
return 'unknown'
def cmdinfo(self, begin, debug=False): def cmdinfo(self, begin, debug=False):
out = [] out = []
if begin: if begin:
self.cmd1 = dict() self.cmd1 = dict()
for cargs in self.infocmds: for cargs in self.infocmds:
delta, name = cargs[0], cargs[1] delta, name, args = cargs[0], cargs[1], cargs[2:]
cmdline, cmdpath = ' '.join(cargs[2:]), self.getExec(cargs[2]) for i in range(len(args)):
if args[i][0] == '{' and args[i][-1] == '}':
args[i] = self.cmdinfovar(args[i][1:-1])
cmdline, cmdpath = ' '.join(args[0:]), self.getExec(args[0])
if not cmdpath or (begin and not delta): if not cmdpath or (begin and not delta):
continue continue
self.dlog('[%s]' % cmdline) self.dlog('[%s]' % cmdline)
try: try:
fp = Popen([cmdpath]+cargs[3:], stdout=PIPE, stderr=PIPE).stdout fp = Popen([cmdpath]+args[1:], stdout=PIPE, stderr=PIPE).stdout
info = ascii(fp.read()).strip() info = ascii(fp.read()).strip()
fp.close() fp.close()
except: except:
...@@ -1452,6 +1470,7 @@ class Data: ...@@ -1452,6 +1470,7 @@ class Data:
errlist = { errlist = {
'HWERROR' : r'.*\[ *Hardware Error *\].*', 'HWERROR' : r'.*\[ *Hardware Error *\].*',
'FWBUG' : r'.*\[ *Firmware Bug *\].*', 'FWBUG' : r'.*\[ *Firmware Bug *\].*',
'TASKFAIL': r'.*Freezing .*after *.*',
'BUG' : r'(?i).*\bBUG\b.*', 'BUG' : r'(?i).*\bBUG\b.*',
'ERROR' : r'(?i).*\bERROR\b.*', 'ERROR' : r'(?i).*\bERROR\b.*',
'WARNING' : r'(?i).*\bWARNING\b.*', 'WARNING' : r'(?i).*\bWARNING\b.*',
...@@ -1462,7 +1481,6 @@ class Data: ...@@ -1462,7 +1481,6 @@ 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 .*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-]*',
...@@ -1602,7 +1620,7 @@ class Data: ...@@ -1602,7 +1620,7 @@ class Data:
pend = self.dmesg[phase]['end'] pend = self.dmesg[phase]['end']
if start <= pend: if start <= pend:
return phase return phase
return 'resume_complete' return 'resume_complete' if 'resume_complete' in self.dmesg else ''
def sourceDevice(self, phaselist, start, end, pid, type): def sourceDevice(self, phaselist, start, end, pid, type):
tgtdev = '' tgtdev = ''
for phase in phaselist: for phase in phaselist:
...@@ -1645,6 +1663,8 @@ class Data: ...@@ -1645,6 +1663,8 @@ class Data:
else: else:
threadname = '%s-%d' % (proc, pid) threadname = '%s-%d' % (proc, pid)
tgtphase = self.sourcePhase(start) tgtphase = self.sourcePhase(start)
if not tgtphase:
return False
self.newAction(tgtphase, threadname, pid, '', start, end, '', ' kth', '') self.newAction(tgtphase, threadname, pid, '', start, end, '', ' kth', '')
return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata) return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata)
# this should not happen # this should not happen
...@@ -1835,9 +1855,9 @@ class Data: ...@@ -1835,9 +1855,9 @@ class Data:
hwr = self.hwend - timedelta(microseconds=rtime) hwr = self.hwend - timedelta(microseconds=rtime)
self.tLow.append('%.0f'%((hwr - hws).total_seconds() * 1000)) self.tLow.append('%.0f'%((hwr - hws).total_seconds() * 1000))
def getTimeValues(self): def getTimeValues(self):
sktime = (self.tSuspended - self.tKernSus) * 1000 s = (self.tSuspended - self.tKernSus) * 1000
rktime = (self.tKernRes - self.tResumed) * 1000 r = (self.tKernRes - self.tResumed) * 1000
return (sktime, rktime) return (max(s, 0), max(r, 0))
def setPhase(self, phase, ktime, isbegin, order=-1): def setPhase(self, phase, ktime, isbegin, order=-1):
if(isbegin): if(isbegin):
# phase start over current phase # phase start over current phase
...@@ -3961,7 +3981,7 @@ def parseKernelLog(data): ...@@ -3961,7 +3981,7 @@ def parseKernelLog(data):
'suspend_machine': ['PM: suspend-to-idle', 'suspend_machine': ['PM: suspend-to-idle',
'PM: noirq suspend of devices complete after.*', 'PM: noirq suspend of devices complete after.*',
'PM: noirq freeze of devices complete after.*'], 'PM: noirq freeze of devices complete after.*'],
'resume_machine': ['PM: Timekeeping suspended for.*', 'resume_machine': ['[PM: ]*Timekeeping suspended for.*',
'ACPI: Low-level resume complete.*', 'ACPI: Low-level resume complete.*',
'ACPI: resume from mwait', 'ACPI: resume from mwait',
'Suspended for [0-9\.]* seconds'], 'Suspended for [0-9\.]* seconds'],
...@@ -3979,14 +3999,14 @@ def parseKernelLog(data): ...@@ -3979,14 +3999,14 @@ def parseKernelLog(data):
# action table (expected events that occur and show up in dmesg) # action table (expected events that occur and show up in dmesg)
at = { at = {
'sync_filesystems': { 'sync_filesystems': {
'smsg': 'PM: Syncing filesystems.*', 'smsg': '.*[Ff]+ilesystems.*',
'emsg': 'PM: Preparing system for mem sleep.*' }, 'emsg': 'PM: Preparing system for[a-z]* sleep.*' },
'freeze_user_processes': { 'freeze_user_processes': {
'smsg': 'Freezing user space processes .*', 'smsg': 'Freezing user space processes.*',
'emsg': 'Freezing remaining freezable tasks.*' }, 'emsg': 'Freezing remaining freezable tasks.*' },
'freeze_tasks': { 'freeze_tasks': {
'smsg': 'Freezing remaining freezable tasks.*', 'smsg': 'Freezing remaining freezable tasks.*',
'emsg': 'PM: Entering (?P<mode>[a-z,A-Z]*) sleep.*' }, 'emsg': 'PM: Suspending system.*' },
'ACPI prepare': { 'ACPI prepare': {
'smsg': 'ACPI: Preparing to enter system sleep state.*', 'smsg': 'ACPI: Preparing to enter system sleep state.*',
'emsg': 'PM: Saving platform NVS memory.*' }, 'emsg': 'PM: Saving platform NVS memory.*' },
...@@ -4120,10 +4140,9 @@ def parseKernelLog(data): ...@@ -4120,10 +4140,9 @@ def parseKernelLog(data):
for a in sorted(at): for a in sorted(at):
if(re.match(at[a]['smsg'], msg)): if(re.match(at[a]['smsg'], msg)):
if(a not in actions): if(a not in actions):
actions[a] = [] actions[a] = [{'begin': ktime, 'end': ktime}]
actions[a].append({'begin': ktime, 'end': ktime})
if(re.match(at[a]['emsg'], msg)): if(re.match(at[a]['emsg'], msg)):
if(a in actions): if(a in actions and actions[a][-1]['begin'] == actions[a][-1]['end']):
actions[a][-1]['end'] = ktime actions[a][-1]['end'] = ktime
# now look for CPU on/off events # now look for CPU on/off events
if(re.match('Disabling non-boot CPUs .*', msg)): if(re.match('Disabling non-boot CPUs .*', msg)):
...@@ -4132,9 +4151,12 @@ def parseKernelLog(data): ...@@ -4132,9 +4151,12 @@ def parseKernelLog(data):
elif(re.match('Enabling non-boot CPUs .*', msg)): elif(re.match('Enabling non-boot CPUs .*', msg)):
# start of first cpu resume # start of first cpu resume
cpu_start = ktime cpu_start = ktime
elif(re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg)): elif(re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg)) \
or re.match('psci: CPU(?P<cpu>[0-9]*) killed.*', msg)):
# end of a cpu suspend, start of the next # end of a cpu suspend, start of the next
m = re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg) m = re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg)
if(not m):
m = re.match('psci: CPU(?P<cpu>[0-9]*) killed.*', msg)
cpu = 'CPU'+m.group('cpu') cpu = 'CPU'+m.group('cpu')
if(cpu not in actions): if(cpu not in actions):
actions[cpu] = [] actions[cpu] = []
......
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