Merge branches 'pm-opp', 'pm-misc', 'pm-avs' and 'pm-tools'
* pm-opp: opp: Don't use IS_ERR on invalid supplies opp: Make dev_pm_opp_set_rate() handle freq = 0 to drop performance votes opp: Don't overwrite rounded clk rate opp: Allocate genpd_virt_devs from dev_pm_opp_attach_genpd() opp: Attach genpds to devices from within OPP core * pm-misc: PM / clk: Remove error message on out-of-memory condition drivers: base: power: clock_ops: Use of_clk_get_parent_count() * pm-avs: power: avs: smartreflex: no need to check return value of debugfs_create functions * pm-tools: cpupower : frequency-set -r option misses the last cpu in related cpu list cpupower: correct spelling of interval Add README and update pm-graph and sleepgraph docs Update to pm-graph 5.4 Update to pm-graph 5.3
This commit is contained in:
commit
13b06b78c7
@ -12,6 +12,7 @@
|
|||||||
#include <linux/pm_clock.h>
|
#include <linux/pm_clock.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/clkdev.h>
|
#include <linux/clkdev.h>
|
||||||
|
#include <linux/of_clk.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/pm_domain.h>
|
#include <linux/pm_domain.h>
|
||||||
@ -92,8 +93,6 @@ static int __pm_clk_add(struct device *dev, const char *con_id,
|
|||||||
if (con_id) {
|
if (con_id) {
|
||||||
ce->con_id = kstrdup(con_id, GFP_KERNEL);
|
ce->con_id = kstrdup(con_id, GFP_KERNEL);
|
||||||
if (!ce->con_id) {
|
if (!ce->con_id) {
|
||||||
dev_err(dev,
|
|
||||||
"Not enough memory for clock connection ID.\n");
|
|
||||||
kfree(ce);
|
kfree(ce);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
@ -195,8 +194,7 @@ int of_pm_clk_add_clks(struct device *dev)
|
|||||||
if (!dev || !dev->of_node)
|
if (!dev || !dev->of_node)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
count = of_count_phandle_with_args(dev->of_node, "clocks",
|
count = of_clk_get_parent_count(dev->of_node);
|
||||||
"#clock-cells");
|
|
||||||
if (count <= 0)
|
if (count <= 0)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
@ -682,7 +682,7 @@ static int _set_opp_custom(const struct opp_table *opp_table,
|
|||||||
|
|
||||||
data->old_opp.rate = old_freq;
|
data->old_opp.rate = old_freq;
|
||||||
size = sizeof(*old_supply) * opp_table->regulator_count;
|
size = sizeof(*old_supply) * opp_table->regulator_count;
|
||||||
if (IS_ERR(old_supply))
|
if (!old_supply)
|
||||||
memset(data->old_opp.supplies, 0, size);
|
memset(data->old_opp.supplies, 0, size);
|
||||||
else
|
else
|
||||||
memcpy(data->old_opp.supplies, old_supply, size);
|
memcpy(data->old_opp.supplies, old_supply, size);
|
||||||
@ -708,7 +708,7 @@ static int _set_required_opps(struct device *dev,
|
|||||||
|
|
||||||
/* Single genpd case */
|
/* Single genpd case */
|
||||||
if (!genpd_virt_devs) {
|
if (!genpd_virt_devs) {
|
||||||
pstate = opp->required_opps[0]->pstate;
|
pstate = likely(opp) ? opp->required_opps[0]->pstate : 0;
|
||||||
ret = dev_pm_genpd_set_performance_state(dev, pstate);
|
ret = dev_pm_genpd_set_performance_state(dev, pstate);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "Failed to set performance state of %s: %d (%d)\n",
|
dev_err(dev, "Failed to set performance state of %s: %d (%d)\n",
|
||||||
@ -726,7 +726,7 @@ static int _set_required_opps(struct device *dev,
|
|||||||
mutex_lock(&opp_table->genpd_virt_dev_lock);
|
mutex_lock(&opp_table->genpd_virt_dev_lock);
|
||||||
|
|
||||||
for (i = 0; i < opp_table->required_opp_count; i++) {
|
for (i = 0; i < opp_table->required_opp_count; i++) {
|
||||||
pstate = opp->required_opps[i]->pstate;
|
pstate = likely(opp) ? opp->required_opps[i]->pstate : 0;
|
||||||
|
|
||||||
if (!genpd_virt_devs[i])
|
if (!genpd_virt_devs[i])
|
||||||
continue;
|
continue;
|
||||||
@ -748,29 +748,37 @@ static int _set_required_opps(struct device *dev,
|
|||||||
* @dev: device for which we do this operation
|
* @dev: device for which we do this operation
|
||||||
* @target_freq: frequency to achieve
|
* @target_freq: frequency to achieve
|
||||||
*
|
*
|
||||||
* This configures the power-supplies and clock source to the levels specified
|
* This configures the power-supplies to the levels specified by the OPP
|
||||||
* by the OPP corresponding to the target_freq.
|
* corresponding to the target_freq, and programs the clock to a value <=
|
||||||
|
* target_freq, as rounded by clk_round_rate(). Device wanting to run at fmax
|
||||||
|
* provided by the opp, should have already rounded to the target OPP's
|
||||||
|
* frequency.
|
||||||
*/
|
*/
|
||||||
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
||||||
{
|
{
|
||||||
struct opp_table *opp_table;
|
struct opp_table *opp_table;
|
||||||
unsigned long freq, old_freq;
|
unsigned long freq, old_freq, temp_freq;
|
||||||
struct dev_pm_opp *old_opp, *opp;
|
struct dev_pm_opp *old_opp, *opp;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (unlikely(!target_freq)) {
|
|
||||||
dev_err(dev, "%s: Invalid target frequency %lu\n", __func__,
|
|
||||||
target_freq);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
opp_table = _find_opp_table(dev);
|
opp_table = _find_opp_table(dev);
|
||||||
if (IS_ERR(opp_table)) {
|
if (IS_ERR(opp_table)) {
|
||||||
dev_err(dev, "%s: device opp doesn't exist\n", __func__);
|
dev_err(dev, "%s: device opp doesn't exist\n", __func__);
|
||||||
return PTR_ERR(opp_table);
|
return PTR_ERR(opp_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unlikely(!target_freq)) {
|
||||||
|
if (opp_table->required_opp_tables) {
|
||||||
|
ret = _set_required_opps(dev, opp_table, NULL);
|
||||||
|
} else {
|
||||||
|
dev_err(dev, "target frequency can't be 0\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto put_opp_table;
|
||||||
|
}
|
||||||
|
|
||||||
clk = opp_table->clk;
|
clk = opp_table->clk;
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
dev_err(dev, "%s: No clock available for the device\n",
|
dev_err(dev, "%s: No clock available for the device\n",
|
||||||
@ -793,13 +801,15 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
|||||||
goto put_opp_table;
|
goto put_opp_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
old_opp = _find_freq_ceil(opp_table, &old_freq);
|
temp_freq = old_freq;
|
||||||
|
old_opp = _find_freq_ceil(opp_table, &temp_freq);
|
||||||
if (IS_ERR(old_opp)) {
|
if (IS_ERR(old_opp)) {
|
||||||
dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n",
|
dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n",
|
||||||
__func__, old_freq, PTR_ERR(old_opp));
|
__func__, old_freq, PTR_ERR(old_opp));
|
||||||
}
|
}
|
||||||
|
|
||||||
opp = _find_freq_ceil(opp_table, &freq);
|
temp_freq = freq;
|
||||||
|
opp = _find_freq_ceil(opp_table, &temp_freq);
|
||||||
if (IS_ERR(opp)) {
|
if (IS_ERR(opp)) {
|
||||||
ret = PTR_ERR(opp);
|
ret = PTR_ERR(opp);
|
||||||
dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n",
|
dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n",
|
||||||
@ -1741,91 +1751,137 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper);
|
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper);
|
||||||
|
|
||||||
|
static void _opp_detach_genpd(struct opp_table *opp_table)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
|
||||||
|
for (index = 0; index < opp_table->required_opp_count; index++) {
|
||||||
|
if (!opp_table->genpd_virt_devs[index])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dev_pm_domain_detach(opp_table->genpd_virt_devs[index], false);
|
||||||
|
opp_table->genpd_virt_devs[index] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(opp_table->genpd_virt_devs);
|
||||||
|
opp_table->genpd_virt_devs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* dev_pm_opp_set_genpd_virt_dev - Set virtual genpd device for an index
|
* dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer
|
||||||
* @dev: Consumer device for which the genpd device is getting set.
|
* @dev: Consumer device for which the genpd is getting attached.
|
||||||
* @virt_dev: virtual genpd device.
|
* @names: Null terminated array of pointers containing names of genpd to attach.
|
||||||
* @index: index.
|
|
||||||
*
|
*
|
||||||
* Multiple generic power domains for a device are supported with the help of
|
* Multiple generic power domains for a device are supported with the help of
|
||||||
* virtual genpd devices, which are created for each consumer device - genpd
|
* virtual genpd devices, which are created for each consumer device - genpd
|
||||||
* pair. These are the device structures which are attached to the power domain
|
* pair. These are the device structures which are attached to the power domain
|
||||||
* and are required by the OPP core to set the performance state of the genpd.
|
* and are required by the OPP core to set the performance state of the genpd.
|
||||||
|
* The same API also works for the case where single genpd is available and so
|
||||||
|
* we don't need to support that separately.
|
||||||
*
|
*
|
||||||
* This helper will normally be called by the consumer driver of the device
|
* This helper will normally be called by the consumer driver of the device
|
||||||
* "dev", as only that has details of the genpd devices.
|
* "dev", as only that has details of the genpd names.
|
||||||
*
|
*
|
||||||
* This helper needs to be called once for each of those virtual devices, but
|
* This helper needs to be called once with a list of all genpd to attach.
|
||||||
* only if multiple domains are available for a device. Otherwise the original
|
* Otherwise the original device structure will be used instead by the OPP core.
|
||||||
* device structure will be used instead by the OPP core.
|
|
||||||
*/
|
*/
|
||||||
struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev,
|
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
|
||||||
struct device *virt_dev,
|
|
||||||
int index)
|
|
||||||
{
|
{
|
||||||
struct opp_table *opp_table;
|
struct opp_table *opp_table;
|
||||||
|
struct device *virt_dev;
|
||||||
|
int index, ret = -EINVAL;
|
||||||
|
const char **name = names;
|
||||||
|
|
||||||
opp_table = dev_pm_opp_get_opp_table(dev);
|
opp_table = dev_pm_opp_get_opp_table(dev);
|
||||||
if (!opp_table)
|
if (!opp_table)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
mutex_lock(&opp_table->genpd_virt_dev_lock);
|
/*
|
||||||
|
* If the genpd's OPP table isn't already initialized, parsing of the
|
||||||
if (unlikely(!opp_table->genpd_virt_devs ||
|
* required-opps fail for dev. We should retry this after genpd's OPP
|
||||||
index >= opp_table->required_opp_count ||
|
* table is added.
|
||||||
opp_table->genpd_virt_devs[index])) {
|
*/
|
||||||
|
if (!opp_table->required_opp_count) {
|
||||||
dev_err(dev, "Invalid request to set required device\n");
|
ret = -EPROBE_DEFER;
|
||||||
dev_pm_opp_put_opp_table(opp_table);
|
goto put_table;
|
||||||
mutex_unlock(&opp_table->genpd_virt_dev_lock);
|
}
|
||||||
|
|
||||||
return ERR_PTR(-EINVAL);
|
mutex_lock(&opp_table->genpd_virt_dev_lock);
|
||||||
|
|
||||||
|
opp_table->genpd_virt_devs = kcalloc(opp_table->required_opp_count,
|
||||||
|
sizeof(*opp_table->genpd_virt_devs),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!opp_table->genpd_virt_devs)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
while (*name) {
|
||||||
|
index = of_property_match_string(dev->of_node,
|
||||||
|
"power-domain-names", *name);
|
||||||
|
if (index < 0) {
|
||||||
|
dev_err(dev, "Failed to find power domain: %s (%d)\n",
|
||||||
|
*name, index);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= opp_table->required_opp_count) {
|
||||||
|
dev_err(dev, "Index can't be greater than required-opp-count - 1, %s (%d : %d)\n",
|
||||||
|
*name, opp_table->required_opp_count, index);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opp_table->genpd_virt_devs[index]) {
|
||||||
|
dev_err(dev, "Genpd virtual device already set %s\n",
|
||||||
|
*name);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
virt_dev = dev_pm_domain_attach_by_name(dev, *name);
|
||||||
|
if (IS_ERR(virt_dev)) {
|
||||||
|
ret = PTR_ERR(virt_dev);
|
||||||
|
dev_err(dev, "Couldn't attach to pm_domain: %d\n", ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
opp_table->genpd_virt_devs[index] = virt_dev;
|
||||||
|
name++;
|
||||||
}
|
}
|
||||||
|
|
||||||
opp_table->genpd_virt_devs[index] = virt_dev;
|
|
||||||
mutex_unlock(&opp_table->genpd_virt_dev_lock);
|
mutex_unlock(&opp_table->genpd_virt_dev_lock);
|
||||||
|
|
||||||
return opp_table;
|
return opp_table;
|
||||||
|
|
||||||
|
err:
|
||||||
|
_opp_detach_genpd(opp_table);
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&opp_table->genpd_virt_dev_lock);
|
||||||
|
|
||||||
|
put_table:
|
||||||
|
dev_pm_opp_put_opp_table(opp_table);
|
||||||
|
|
||||||
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dev_pm_opp_attach_genpd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* dev_pm_opp_put_genpd_virt_dev() - Releases resources blocked for genpd device.
|
* dev_pm_opp_detach_genpd() - Detach genpd(s) from the device.
|
||||||
* @opp_table: OPP table returned by dev_pm_opp_set_genpd_virt_dev().
|
* @opp_table: OPP table returned by dev_pm_opp_attach_genpd().
|
||||||
* @virt_dev: virtual genpd device.
|
|
||||||
*
|
*
|
||||||
* This releases the resource previously acquired with a call to
|
* This detaches the genpd(s), resets the virtual device pointers, and puts the
|
||||||
* dev_pm_opp_set_genpd_virt_dev(). The consumer driver shall call this helper
|
* OPP table.
|
||||||
* if it doesn't want OPP core to update performance state of a power domain
|
|
||||||
* anymore.
|
|
||||||
*/
|
*/
|
||||||
void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table,
|
void dev_pm_opp_detach_genpd(struct opp_table *opp_table)
|
||||||
struct device *virt_dev)
|
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting
|
* Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting
|
||||||
* used in parallel.
|
* used in parallel.
|
||||||
*/
|
*/
|
||||||
mutex_lock(&opp_table->genpd_virt_dev_lock);
|
mutex_lock(&opp_table->genpd_virt_dev_lock);
|
||||||
|
_opp_detach_genpd(opp_table);
|
||||||
for (i = 0; i < opp_table->required_opp_count; i++) {
|
|
||||||
if (opp_table->genpd_virt_devs[i] != virt_dev)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
opp_table->genpd_virt_devs[i] = NULL;
|
|
||||||
dev_pm_opp_put_opp_table(opp_table);
|
|
||||||
|
|
||||||
/* Drop the vote */
|
|
||||||
dev_pm_genpd_set_performance_state(virt_dev, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_unlock(&opp_table->genpd_virt_dev_lock);
|
mutex_unlock(&opp_table->genpd_virt_dev_lock);
|
||||||
|
|
||||||
if (unlikely(i == opp_table->required_opp_count))
|
dev_pm_opp_put_opp_table(opp_table);
|
||||||
dev_err(virt_dev, "Failed to find required device entry\n");
|
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dev_pm_opp_detach_genpd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table.
|
* dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table.
|
||||||
|
@ -138,7 +138,6 @@ static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np)
|
|||||||
static void _opp_table_free_required_tables(struct opp_table *opp_table)
|
static void _opp_table_free_required_tables(struct opp_table *opp_table)
|
||||||
{
|
{
|
||||||
struct opp_table **required_opp_tables = opp_table->required_opp_tables;
|
struct opp_table **required_opp_tables = opp_table->required_opp_tables;
|
||||||
struct device **genpd_virt_devs = opp_table->genpd_virt_devs;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!required_opp_tables)
|
if (!required_opp_tables)
|
||||||
@ -152,10 +151,8 @@ static void _opp_table_free_required_tables(struct opp_table *opp_table)
|
|||||||
}
|
}
|
||||||
|
|
||||||
kfree(required_opp_tables);
|
kfree(required_opp_tables);
|
||||||
kfree(genpd_virt_devs);
|
|
||||||
|
|
||||||
opp_table->required_opp_count = 0;
|
opp_table->required_opp_count = 0;
|
||||||
opp_table->genpd_virt_devs = NULL;
|
|
||||||
opp_table->required_opp_tables = NULL;
|
opp_table->required_opp_tables = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,9 +165,8 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
|
|||||||
struct device_node *opp_np)
|
struct device_node *opp_np)
|
||||||
{
|
{
|
||||||
struct opp_table **required_opp_tables;
|
struct opp_table **required_opp_tables;
|
||||||
struct device **genpd_virt_devs = NULL;
|
|
||||||
struct device_node *required_np, *np;
|
struct device_node *required_np, *np;
|
||||||
int count, count_pd, i;
|
int count, i;
|
||||||
|
|
||||||
/* Traversing the first OPP node is all we need */
|
/* Traversing the first OPP node is all we need */
|
||||||
np = of_get_next_available_child(opp_np, NULL);
|
np = of_get_next_available_child(opp_np, NULL);
|
||||||
@ -183,33 +179,11 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
|
|||||||
if (!count)
|
if (!count)
|
||||||
goto put_np;
|
goto put_np;
|
||||||
|
|
||||||
/*
|
|
||||||
* Check the number of power-domains to know if we need to deal
|
|
||||||
* with virtual devices. In some cases we have devices with multiple
|
|
||||||
* power domains but with only one of them being scalable, hence
|
|
||||||
* 'count' could be 1, but we still have to deal with multiple genpds
|
|
||||||
* and virtual devices.
|
|
||||||
*/
|
|
||||||
count_pd = of_count_phandle_with_args(dev->of_node, "power-domains",
|
|
||||||
"#power-domain-cells");
|
|
||||||
if (!count_pd)
|
|
||||||
goto put_np;
|
|
||||||
|
|
||||||
if (count_pd > 1) {
|
|
||||||
genpd_virt_devs = kcalloc(count, sizeof(*genpd_virt_devs),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!genpd_virt_devs)
|
|
||||||
goto put_np;
|
|
||||||
}
|
|
||||||
|
|
||||||
required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
|
required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!required_opp_tables) {
|
if (!required_opp_tables)
|
||||||
kfree(genpd_virt_devs);
|
|
||||||
goto put_np;
|
goto put_np;
|
||||||
}
|
|
||||||
|
|
||||||
opp_table->genpd_virt_devs = genpd_virt_devs;
|
|
||||||
opp_table->required_opp_tables = required_opp_tables;
|
opp_table->required_opp_tables = required_opp_tables;
|
||||||
opp_table->required_opp_count = count;
|
opp_table->required_opp_count = count;
|
||||||
|
|
||||||
|
@ -899,38 +899,19 @@ static int omap_sr_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
dev_info(&pdev->dev, "%s: SmartReflex driver initialized\n", __func__);
|
dev_info(&pdev->dev, "%s: SmartReflex driver initialized\n", __func__);
|
||||||
if (!sr_dbg_dir) {
|
if (!sr_dbg_dir)
|
||||||
sr_dbg_dir = debugfs_create_dir("smartreflex", NULL);
|
sr_dbg_dir = debugfs_create_dir("smartreflex", NULL);
|
||||||
if (IS_ERR_OR_NULL(sr_dbg_dir)) {
|
|
||||||
ret = PTR_ERR(sr_dbg_dir);
|
|
||||||
pr_err("%s:sr debugfs dir creation failed(%d)\n",
|
|
||||||
__func__, ret);
|
|
||||||
goto err_list_del;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sr_info->dbg_dir = debugfs_create_dir(sr_info->name, sr_dbg_dir);
|
sr_info->dbg_dir = debugfs_create_dir(sr_info->name, sr_dbg_dir);
|
||||||
if (IS_ERR_OR_NULL(sr_info->dbg_dir)) {
|
|
||||||
dev_err(&pdev->dev, "%s: Unable to create debugfs directory\n",
|
|
||||||
__func__);
|
|
||||||
ret = PTR_ERR(sr_info->dbg_dir);
|
|
||||||
goto err_debugfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
(void) debugfs_create_file("autocomp", S_IRUGO | S_IWUSR,
|
debugfs_create_file("autocomp", S_IRUGO | S_IWUSR, sr_info->dbg_dir,
|
||||||
sr_info->dbg_dir, (void *)sr_info, &pm_sr_fops);
|
(void *)sr_info, &pm_sr_fops);
|
||||||
(void) debugfs_create_x32("errweight", S_IRUGO, sr_info->dbg_dir,
|
debugfs_create_x32("errweight", S_IRUGO, sr_info->dbg_dir,
|
||||||
&sr_info->err_weight);
|
&sr_info->err_weight);
|
||||||
(void) debugfs_create_x32("errmaxlimit", S_IRUGO, sr_info->dbg_dir,
|
debugfs_create_x32("errmaxlimit", S_IRUGO, sr_info->dbg_dir,
|
||||||
&sr_info->err_maxlimit);
|
&sr_info->err_maxlimit);
|
||||||
|
|
||||||
nvalue_dir = debugfs_create_dir("nvalue", sr_info->dbg_dir);
|
nvalue_dir = debugfs_create_dir("nvalue", sr_info->dbg_dir);
|
||||||
if (IS_ERR_OR_NULL(nvalue_dir)) {
|
|
||||||
dev_err(&pdev->dev, "%s: Unable to create debugfs directory for n-values\n",
|
|
||||||
__func__);
|
|
||||||
ret = PTR_ERR(nvalue_dir);
|
|
||||||
goto err_debugfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sr_info->nvalue_count == 0 || !sr_info->nvalue_table) {
|
if (sr_info->nvalue_count == 0 || !sr_info->nvalue_table) {
|
||||||
dev_warn(&pdev->dev, "%s: %s: No Voltage table for the corresponding vdd. Cannot create debugfs entries for n-values\n",
|
dev_warn(&pdev->dev, "%s: %s: No Voltage table for the corresponding vdd. Cannot create debugfs entries for n-values\n",
|
||||||
@ -945,12 +926,12 @@ static int omap_sr_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
snprintf(name, sizeof(name), "volt_%lu",
|
snprintf(name, sizeof(name), "volt_%lu",
|
||||||
sr_info->nvalue_table[i].volt_nominal);
|
sr_info->nvalue_table[i].volt_nominal);
|
||||||
(void) debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir,
|
debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir,
|
||||||
&(sr_info->nvalue_table[i].nvalue));
|
&(sr_info->nvalue_table[i].nvalue));
|
||||||
snprintf(name, sizeof(name), "errminlimit_%lu",
|
snprintf(name, sizeof(name), "errminlimit_%lu",
|
||||||
sr_info->nvalue_table[i].volt_nominal);
|
sr_info->nvalue_table[i].volt_nominal);
|
||||||
(void) debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir,
|
debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir,
|
||||||
&(sr_info->nvalue_table[i].errminlimit));
|
&(sr_info->nvalue_table[i].errminlimit));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,8 +128,8 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);
|
|||||||
void dev_pm_opp_put_clkname(struct opp_table *opp_table);
|
void dev_pm_opp_put_clkname(struct opp_table *opp_table);
|
||||||
struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
|
struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
|
||||||
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
|
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
|
||||||
struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index);
|
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names);
|
||||||
void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev);
|
void dev_pm_opp_detach_genpd(struct opp_table *opp_table);
|
||||||
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
|
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
|
||||||
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
|
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
|
||||||
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
|
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
|
||||||
@ -292,12 +292,12 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const
|
|||||||
|
|
||||||
static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {}
|
static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {}
|
||||||
|
|
||||||
static inline struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index)
|
static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
|
||||||
{
|
{
|
||||||
return ERR_PTR(-ENOTSUPP);
|
return ERR_PTR(-ENOTSUPP);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev) {}
|
static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {}
|
||||||
|
|
||||||
static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate)
|
static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate)
|
||||||
{
|
{
|
||||||
|
@ -61,7 +61,7 @@ Only display specific monitors. Use the monitor string(s) provided by \-l option
|
|||||||
.PP
|
.PP
|
||||||
\-i seconds
|
\-i seconds
|
||||||
.RS 4
|
.RS 4
|
||||||
Measure intervall.
|
Measure interval.
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-c
|
\-c
|
||||||
|
@ -98,7 +98,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: utils/idle_monitor/cpupower-monitor.c:74
|
#: utils/idle_monitor/cpupower-monitor.c:74
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "\t -i: time intervall to measure for in seconds (default 1)\n"
|
msgid "\t -i: time interval to measure for in seconds (default 1)\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: utils/idle_monitor/cpupower-monitor.c:75
|
#: utils/idle_monitor/cpupower-monitor.c:75
|
||||||
|
@ -95,7 +95,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: utils/idle_monitor/cpupower-monitor.c:74
|
#: utils/idle_monitor/cpupower-monitor.c:74
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "\t -i: time intervall to measure for in seconds (default 1)\n"
|
msgid "\t -i: time interval to measure for in seconds (default 1)\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: utils/idle_monitor/cpupower-monitor.c:75
|
#: utils/idle_monitor/cpupower-monitor.c:75
|
||||||
|
@ -95,7 +95,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: utils/idle_monitor/cpupower-monitor.c:74
|
#: utils/idle_monitor/cpupower-monitor.c:74
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "\t -i: time intervall to measure for in seconds (default 1)\n"
|
msgid "\t -i: time interval to measure for in seconds (default 1)\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: utils/idle_monitor/cpupower-monitor.c:75
|
#: utils/idle_monitor/cpupower-monitor.c:75
|
||||||
|
@ -95,7 +95,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: utils/idle_monitor/cpupower-monitor.c:74
|
#: utils/idle_monitor/cpupower-monitor.c:74
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "\t -i: time intervall to measure for in seconds (default 1)\n"
|
msgid "\t -i: time interval to measure for in seconds (default 1)\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: utils/idle_monitor/cpupower-monitor.c:75
|
#: utils/idle_monitor/cpupower-monitor.c:75
|
||||||
|
@ -93,7 +93,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: utils/idle_monitor/cpupower-monitor.c:74
|
#: utils/idle_monitor/cpupower-monitor.c:74
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "\t -i: time intervall to measure for in seconds (default 1)\n"
|
msgid "\t -i: time interval to measure for in seconds (default 1)\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: utils/idle_monitor/cpupower-monitor.c:75
|
#: utils/idle_monitor/cpupower-monitor.c:75
|
||||||
|
@ -305,6 +305,8 @@ int cmd_freq_set(int argc, char **argv)
|
|||||||
bitmask_setbit(cpus_chosen, cpus->cpu);
|
bitmask_setbit(cpus_chosen, cpus->cpu);
|
||||||
cpus = cpus->next;
|
cpus = cpus->next;
|
||||||
}
|
}
|
||||||
|
/* Set the last cpu in related cpus list */
|
||||||
|
bitmask_setbit(cpus_chosen, cpus->cpu);
|
||||||
cpufreq_put_related_cpus(cpus);
|
cpufreq_put_related_cpus(cpus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
552
tools/power/pm-graph/README
Normal file
552
tools/power/pm-graph/README
Normal file
@ -0,0 +1,552 @@
|
|||||||
|
p m - g r a p h
|
||||||
|
|
||||||
|
pm-graph: suspend/resume/boot timing analysis tools
|
||||||
|
Version: 5.4
|
||||||
|
Author: Todd Brandt <todd.e.brandt@intel.com>
|
||||||
|
Home Page: https://01.org/pm-graph
|
||||||
|
|
||||||
|
Report bugs/issues at bugzilla.kernel.org Tools/pm-graph
|
||||||
|
- https://bugzilla.kernel.org/buglist.cgi?component=pm-graph&product=Tools
|
||||||
|
|
||||||
|
Full documentation available online & in man pages
|
||||||
|
- Getting Started:
|
||||||
|
https://01.org/pm-graph/documentation/getting-started
|
||||||
|
|
||||||
|
- Config File Format:
|
||||||
|
https://01.org/pm-graph/documentation/3-config-file-format
|
||||||
|
|
||||||
|
- upstream version in git:
|
||||||
|
https://github.com/intel/pm-graph/
|
||||||
|
|
||||||
|
Table of Contents
|
||||||
|
- Overview
|
||||||
|
- Setup
|
||||||
|
- Usage
|
||||||
|
- Basic Usage
|
||||||
|
- Dev Mode Usage
|
||||||
|
- Proc Mode Usage
|
||||||
|
- Configuration Files
|
||||||
|
- Usage Examples
|
||||||
|
- Config File Options
|
||||||
|
- Custom Timeline Entries
|
||||||
|
- Adding/Editing Timeline Functions
|
||||||
|
- Adding/Editing Dev Timeline Source Functions
|
||||||
|
- Verifying your Custom Functions
|
||||||
|
- Testing on consumer linux Operating Systems
|
||||||
|
- Android
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
| OVERVIEW |
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
This tool suite is designed to assist kernel and OS developers in optimizing
|
||||||
|
their linux stack's suspend/resume & boot time. Using a kernel image built
|
||||||
|
with a few extra options enabled, the tools will execute a suspend or boot,
|
||||||
|
and will capture dmesg and ftrace data. This data is transformed into a set of
|
||||||
|
timelines and a callgraph to give a quick and detailed view of which devices
|
||||||
|
and kernel processes are taking the most time in suspend/resume & boot.
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
| SETUP |
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
These packages are required to execute the scripts
|
||||||
|
- python
|
||||||
|
- python-requests
|
||||||
|
|
||||||
|
Ubuntu:
|
||||||
|
sudo apt-get install python python-requests
|
||||||
|
|
||||||
|
Fedora:
|
||||||
|
sudo dnf install python python-requests
|
||||||
|
|
||||||
|
The tools can most easily be installed via git clone and make install
|
||||||
|
|
||||||
|
$> git clone http://github.com/intel/pm-graph.git
|
||||||
|
$> cd pm-graph
|
||||||
|
$> sudo make install
|
||||||
|
$> man sleepgraph ; man bootgraph
|
||||||
|
|
||||||
|
Setup involves some minor kernel configuration
|
||||||
|
|
||||||
|
The following kernel build options are required for all kernels:
|
||||||
|
CONFIG_DEVMEM=y
|
||||||
|
CONFIG_PM_DEBUG=y
|
||||||
|
CONFIG_PM_SLEEP_DEBUG=y
|
||||||
|
CONFIG_FTRACE=y
|
||||||
|
CONFIG_FUNCTION_TRACER=y
|
||||||
|
CONFIG_FUNCTION_GRAPH_TRACER=y
|
||||||
|
CONFIG_KPROBES=y
|
||||||
|
CONFIG_KPROBES_ON_FTRACE=y
|
||||||
|
|
||||||
|
In kernel 3.15.0, two patches were upstreamed which enable the
|
||||||
|
v3.0 behavior. These patches allow the tool to read all the
|
||||||
|
data from trace events instead of from dmesg. You can enable
|
||||||
|
this behavior on earlier kernels with these patches:
|
||||||
|
|
||||||
|
(kernel/pre-3.15/enable_trace_events_suspend_resume.patch)
|
||||||
|
(kernel/pre-3.15/enable_trace_events_device_pm_callback.patch)
|
||||||
|
|
||||||
|
If you're using a kernel older than 3.15.0, the following
|
||||||
|
additional kernel parameters are required:
|
||||||
|
(e.g. in file /etc/default/grub)
|
||||||
|
GRUB_CMDLINE_LINUX_DEFAULT="... initcall_debug log_buf_len=32M ..."
|
||||||
|
|
||||||
|
If you're using a kernel older than 3.11-rc2, the following simple
|
||||||
|
patch must be applied to enable ftrace data:
|
||||||
|
in file: kernel/power/suspend.c
|
||||||
|
in function: int suspend_devices_and_enter(suspend_state_t state)
|
||||||
|
remove call to "ftrace_stop();"
|
||||||
|
remove call to "ftrace_start();"
|
||||||
|
|
||||||
|
There is a patch which does this for kernel v3.8.0:
|
||||||
|
(kernel/pre-3.11-rc2/enable_ftrace_in_suspendresume.patch)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
| USAGE |
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
Basic Usage
|
||||||
|
___________
|
||||||
|
|
||||||
|
1) First configure a kernel using the instructions from the previous sections.
|
||||||
|
Then build, install, and boot with it.
|
||||||
|
2) Open up a terminal window and execute the mode list command:
|
||||||
|
|
||||||
|
%> sudo ./sleepgraph.py -modes
|
||||||
|
['freeze', 'mem', 'disk']
|
||||||
|
|
||||||
|
Execute a test using one of the available power modes, e.g. mem (S3):
|
||||||
|
|
||||||
|
%> sudo ./sleepgraph.py -m mem -rtcwake 15
|
||||||
|
|
||||||
|
or with a config file
|
||||||
|
|
||||||
|
%> sudo ./sleepgraph.py -config config/suspend.cfg
|
||||||
|
|
||||||
|
When the system comes back you'll see the script finishing up and
|
||||||
|
creating the output files in the test subdir. It generates output
|
||||||
|
files in subdirectory: suspend-mmddyy-HHMMSS. The ftrace file can
|
||||||
|
be used to regenerate the html timeline with different options
|
||||||
|
|
||||||
|
HTML output: <hostname>_<mode>.html
|
||||||
|
raw dmesg output: <hostname>_<mode>_dmesg.txt
|
||||||
|
raw ftrace output: <hostname>_<mode>_ftrace.txt
|
||||||
|
|
||||||
|
View the html in firefox or chrome.
|
||||||
|
|
||||||
|
|
||||||
|
Dev Mode Usage
|
||||||
|
______________
|
||||||
|
|
||||||
|
Developer mode adds information on low level source calls to the timeline.
|
||||||
|
The tool sets kprobes on all delay and mutex calls to see which devices
|
||||||
|
are waiting for something and when. It also sets a suite of kprobes on
|
||||||
|
subsystem dependent calls to better fill out the timeline.
|
||||||
|
|
||||||
|
The tool will also expose kernel threads that don't normally show up in the
|
||||||
|
timeline. This is useful in discovering dependent threads to get a better
|
||||||
|
idea of what each device is waiting for. For instance, the scsi_eh thread,
|
||||||
|
a.k.a. scsi resume error handler, is what each SATA disk device waits for
|
||||||
|
before it can continue resume.
|
||||||
|
|
||||||
|
The timeline will be much larger if run with dev mode, so it can be useful
|
||||||
|
to set the -mindev option to clip out any device blocks that are too small
|
||||||
|
to see easily. The following command will give a nice dev mode run:
|
||||||
|
|
||||||
|
%> sudo ./sleepgraph.py -m mem -rtcwake 15 -mindev 1 -dev
|
||||||
|
|
||||||
|
or with a config file
|
||||||
|
|
||||||
|
%> sudo ./sleepgraph.py -config config/suspend-dev.cfg
|
||||||
|
|
||||||
|
|
||||||
|
Proc Mode Usage
|
||||||
|
_______________
|
||||||
|
|
||||||
|
Proc mode adds user process info to the timeline. This is done in a manner
|
||||||
|
similar to the bootchart utility, which graphs init processes and their
|
||||||
|
execution as the system boots. This tool option does the same thing but for
|
||||||
|
the period before and after suspend/resume.
|
||||||
|
|
||||||
|
In order to see any process info, there needs to be some delay before or
|
||||||
|
after resume since processes are frozen in suspend_prepare and thawed in
|
||||||
|
resume_complete. The predelay and postdelay args allow you to do this. It
|
||||||
|
can also be useful to run in x2 mode with an x2 delay, this way you can
|
||||||
|
see process activity before and after resume, and in between two
|
||||||
|
successive suspend/resumes.
|
||||||
|
|
||||||
|
The command can be run like this:
|
||||||
|
|
||||||
|
%> sudo ./sleepgraph.py -m mem -rtcwake 15 -x2 -x2delay 1000 -predelay 1000 -postdelay 1000 -proc
|
||||||
|
|
||||||
|
or with a config file
|
||||||
|
|
||||||
|
%> sudo ./sleepgraph.py -config config/suspend-proc.cfg
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
| CONFIGURATION FILES |
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
Since 4.0 we've moved to using config files in lieu of command line options.
|
||||||
|
The config folder contains a collection of typical use cases.
|
||||||
|
There are corresponding configs for other power modes:
|
||||||
|
|
||||||
|
Simple suspend/resume with basic timeline (mem/freeze/standby)
|
||||||
|
config/suspend.cfg
|
||||||
|
config/freeze.cfg
|
||||||
|
config/standby.cfg
|
||||||
|
|
||||||
|
Dev mode suspend/resume with dev timeline (mem/freeze/standby)
|
||||||
|
config/suspend-dev.cfg
|
||||||
|
config/freeze-dev.cfg
|
||||||
|
config/standby-dev.cfg
|
||||||
|
|
||||||
|
Simple suspend/resume with timeline and callgraph (mem/freeze/standby)
|
||||||
|
config/suspend-callgraph.cfg
|
||||||
|
config/freeze-callgraph.cfg
|
||||||
|
config/standby-callgraph.cfg
|
||||||
|
|
||||||
|
Sample proc mode x2 run using mem suspend
|
||||||
|
config/suspend-x2-proc.cfg
|
||||||
|
|
||||||
|
Sample for editing timeline funcs (moves internal functions into config)
|
||||||
|
config/custom-timeline-functions.cfg
|
||||||
|
|
||||||
|
Sample debug config for serio subsystem
|
||||||
|
config/debug-serio-suspend.cfg
|
||||||
|
|
||||||
|
|
||||||
|
Usage Examples
|
||||||
|
______________
|
||||||
|
|
||||||
|
Run a simple mem suspend:
|
||||||
|
%> sudo ./sleepgraph.py -config config/suspend.cfg
|
||||||
|
|
||||||
|
Run a mem suspend with callgraph data:
|
||||||
|
%> sudo ./sleepgraph.py -config config/suspend-callgraph.cfg
|
||||||
|
|
||||||
|
Run a mem suspend with dev mode detail:
|
||||||
|
%> sudo ./sleepgraph.py -config config/suspend-dev.cfg
|
||||||
|
|
||||||
|
|
||||||
|
Config File Options
|
||||||
|
___________________
|
||||||
|
|
||||||
|
[Settings]
|
||||||
|
|
||||||
|
# Verbosity: print verbose messages (def: false)
|
||||||
|
verbose: false
|
||||||
|
|
||||||
|
# Suspend Mode: e.g. standby, mem, freeze, disk (def: mem)
|
||||||
|
mode: mem
|
||||||
|
|
||||||
|
# Output Directory Format: {hostname}, {date}, {time} give current values
|
||||||
|
output-dir: suspend-{hostname}-{date}-{time}
|
||||||
|
|
||||||
|
# Automatic Wakeup: use rtcwake to wakeup after X seconds (def: infinity)
|
||||||
|
rtcwake: 15
|
||||||
|
|
||||||
|
# Add Logs: add the dmesg and ftrace log to the html output (def: false)
|
||||||
|
addlogs: false
|
||||||
|
|
||||||
|
# Sus/Res Gap: insert a gap between sus & res in the timeline (def: false)
|
||||||
|
srgap: false
|
||||||
|
|
||||||
|
# Custom Command: Command to execute in lieu of suspend (def: "")
|
||||||
|
command: echo mem > /sys/power/state
|
||||||
|
|
||||||
|
# Proc mode: graph user processes and cpu usage in the timeline (def: false)
|
||||||
|
proc: false
|
||||||
|
|
||||||
|
# Dev mode: graph source functions in the timeline (def: false)
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
# Suspend/Resume x2: run 2 suspend/resumes back to back (def: false)
|
||||||
|
x2: false
|
||||||
|
|
||||||
|
# x2 Suspend Delay: time delay between the two test runs in ms (def: 0 ms)
|
||||||
|
x2delay: 0
|
||||||
|
|
||||||
|
# Pre Suspend Delay: nclude an N ms delay before (1st) suspend (def: 0 ms)
|
||||||
|
predelay: 0
|
||||||
|
|
||||||
|
# Post Resume Delay: include an N ms delay after (last) resume (def: 0 ms)
|
||||||
|
postdelay: 0
|
||||||
|
|
||||||
|
# Min Device Length: graph only dev callbacks longer than min (def: 0.001 ms)
|
||||||
|
mindev: 0.001
|
||||||
|
|
||||||
|
# Callgraph: gather ftrace callgraph data on all timeline events (def: false)
|
||||||
|
callgraph: false
|
||||||
|
|
||||||
|
# Expand Callgraph: pre-expand the callgraph treeviews in html (def: false)
|
||||||
|
expandcg: false
|
||||||
|
|
||||||
|
# Min Callgraph Length: show callgraphs only if longer than min (def: 1 ms)
|
||||||
|
mincg: 1
|
||||||
|
|
||||||
|
# Timestamp Precision: number of sig digits in timestamps (0:S, [3:ms], 6:us)
|
||||||
|
timeprec: 3
|
||||||
|
|
||||||
|
# Device Filter: show only devs whose name/driver includes one of these strings
|
||||||
|
devicefilter: _cpu_up,_cpu_down,i915,usb
|
||||||
|
|
||||||
|
# Override default timeline entries:
|
||||||
|
# Do not use the internal default functions for timeline entries (def: false)
|
||||||
|
# Set this to true if you intend to only use the ones defined in the config
|
||||||
|
override-timeline-functions: true
|
||||||
|
|
||||||
|
# Override default dev timeline entries:
|
||||||
|
# Do not use the internal default functions for dev timeline entries (def: false)
|
||||||
|
# Set this to true if you intend to only use the ones defined in the config
|
||||||
|
override-dev-timeline-functions: true
|
||||||
|
|
||||||
|
# Call Loop Max Gap (dev mode only)
|
||||||
|
# merge loops of the same call if each is less than maxgap apart (def: 100us)
|
||||||
|
callloop-maxgap: 0.0001
|
||||||
|
|
||||||
|
# Call Loop Max Length (dev mode only)
|
||||||
|
# merge loops of the same call if each is less than maxlen in length (def: 5ms)
|
||||||
|
callloop-maxlen: 0.005
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
| CUSTOM TIMELINE ENTRIES |
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
Adding or Editing Timeline Functions
|
||||||
|
____________________________________
|
||||||
|
|
||||||
|
The tool uses an array of function names to fill out empty spaces in the
|
||||||
|
timeline where device callbacks don't appear. For instance, in suspend_prepare
|
||||||
|
the tool adds the sys_sync and freeze_processes calls as virtual device blocks
|
||||||
|
in the timeline to show you where the time is going. These calls should fill
|
||||||
|
the timeline with contiguous data so that most kernel execution is covered.
|
||||||
|
|
||||||
|
It is possible to add new function calls to the timeline by adding them to
|
||||||
|
the config. It's also possible to copy the internal timeline functions into
|
||||||
|
the config so that you can override and edit them. Place them in the
|
||||||
|
timeline_functions_ARCH section with the name of your architecture appended.
|
||||||
|
i.e. for x86_64: [timeline_functions_x86_64]
|
||||||
|
|
||||||
|
Use the override-timeline-functions option if you only want to use your
|
||||||
|
custom calls, or leave it false to append them to the internal ones.
|
||||||
|
|
||||||
|
This section includes a list of functions (set using kprobes) which use both
|
||||||
|
symbol data and function arg data. The args are pulled directly from the
|
||||||
|
stack using this architecture's registers and stack formatting. Each entry
|
||||||
|
can include up to four pieces of info: The function name, a format string,
|
||||||
|
an argument list, and a color. But only a function name is required.
|
||||||
|
|
||||||
|
For a full example config, see config/custom-timeline-functions.cfg. It pulls
|
||||||
|
all the internal timeline functions into the config and allows you to edit
|
||||||
|
them.
|
||||||
|
|
||||||
|
Entry format:
|
||||||
|
|
||||||
|
function: format{fn_arg1}_{fn_arg2} fn_arg1 fn_arg2 ... [color=purple]
|
||||||
|
|
||||||
|
Required Arguments:
|
||||||
|
|
||||||
|
function: The symbol name for the function you want probed, this is the
|
||||||
|
minimum required for an entry, it will show up as the function
|
||||||
|
name with no arguments.
|
||||||
|
|
||||||
|
example: _cpu_up:
|
||||||
|
|
||||||
|
Optional Arguments:
|
||||||
|
|
||||||
|
format: The format to display the data on the timeline in. Use braces to
|
||||||
|
enclose the arg names.
|
||||||
|
|
||||||
|
example: CPU_ON[{cpu}]
|
||||||
|
|
||||||
|
color: The color of the entry block in the timeline. The default color is
|
||||||
|
transparent, so the entry shares the phase color. The color is an
|
||||||
|
html color string, either a word, or an RGB.
|
||||||
|
|
||||||
|
example: [color=#CC00CC]
|
||||||
|
|
||||||
|
arglist: A list of arguments from registers/stack addresses. See URL:
|
||||||
|
https://www.kernel.org/doc/Documentation/trace/kprobetrace.txt
|
||||||
|
|
||||||
|
example: cpu=%di:s32
|
||||||
|
|
||||||
|
Here is a full example entry. It displays cpu resume calls in the timeline
|
||||||
|
in orange. They will appear as CPU_ON[0], CPU_ON[1], etc.
|
||||||
|
|
||||||
|
[timeline_functions_x86_64]
|
||||||
|
_cpu_up: CPU_ON[{cpu}] cpu=%di:s32 [color=orange]
|
||||||
|
|
||||||
|
|
||||||
|
Adding or Editing Dev Mode Timeline Source Functions
|
||||||
|
____________________________________________________
|
||||||
|
|
||||||
|
In dev mode, the tool uses an array of function names to monitor source
|
||||||
|
execution within the timeline entries.
|
||||||
|
|
||||||
|
The function calls are displayed inside the main device/call blocks in the
|
||||||
|
timeline. However, if a function call is not within a main timeline event,
|
||||||
|
it will spawn an entirely new event named after the caller's kernel thread.
|
||||||
|
These asynchronous kernel threads will populate in a separate section
|
||||||
|
beneath the main device/call section.
|
||||||
|
|
||||||
|
The tool has a set of hard coded calls which focus on the most common use
|
||||||
|
cases: msleep, udelay, schedule_timeout, mutex_lock_slowpath, etc. These are
|
||||||
|
the functions that add a hardcoded time delay to the suspend/resume path.
|
||||||
|
The tool also includes some common functions native to important
|
||||||
|
subsystems: ata, i915, and ACPI, etc.
|
||||||
|
|
||||||
|
It is possible to add new function calls to the dev timeline by adding them
|
||||||
|
to the config. It's also possible to copy the internal dev timeline
|
||||||
|
functions into the config so that you can override and edit them. Place them
|
||||||
|
in the dev_timeline_functions_ARCH section with the name of your architecture
|
||||||
|
appended. i.e. for x86_64: [dev_timeline_functions_x86_64]
|
||||||
|
|
||||||
|
Use the override-dev-timeline-functions option if you only want to use your
|
||||||
|
custom calls, or leave it false to append them to the internal ones.
|
||||||
|
|
||||||
|
The format is the same as the timeline_functions_x86_64 section. It's a
|
||||||
|
list of functions (set using kprobes) which use both symbol data and function
|
||||||
|
arg data. The args are pulled directly from the stack using this
|
||||||
|
architecture's registers and stack formatting. Each entry can include up
|
||||||
|
to four pieces of info: The function name, a format string, an argument list,
|
||||||
|
and a color. But only the function name is required.
|
||||||
|
|
||||||
|
For a full example config, see config/custom-timeline-functions.cfg. It pulls
|
||||||
|
all the internal dev timeline functions into the config and allows you to edit
|
||||||
|
them.
|
||||||
|
|
||||||
|
Here is a full example entry. It displays the ATA port reset calls as
|
||||||
|
ataN_port_reset in the timeline. This is where most of the SATA disk resume
|
||||||
|
time goes, so it can be helpful to see the low level call.
|
||||||
|
|
||||||
|
[dev_timeline_functions_x86_64]
|
||||||
|
ata_eh_recover: ata{port}_port_reset port=+36(%di):s32 [color=#CC00CC]
|
||||||
|
|
||||||
|
|
||||||
|
Verifying your custom functions
|
||||||
|
_______________________________
|
||||||
|
|
||||||
|
Once you have a set of functions (kprobes) defined, it can be useful to
|
||||||
|
perform a quick check to see if you formatted them correctly and if the system
|
||||||
|
actually supports them. To do this, run the tool with your config file
|
||||||
|
and the -status option. The tool will go through all the kprobes (both
|
||||||
|
custom and internal if you haven't overridden them) and actually attempts
|
||||||
|
to set them in ftrace. It will then print out success or fail for you.
|
||||||
|
|
||||||
|
Note that kprobes which don't actually exist in the kernel won't stop the
|
||||||
|
tool, they just wont show up.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
sudo ./sleepgraph.py -config config/custom-timeline-functions.cfg -status
|
||||||
|
Checking this system (myhostname)...
|
||||||
|
have root access: YES
|
||||||
|
is sysfs mounted: YES
|
||||||
|
is "mem" a valid power mode: YES
|
||||||
|
is ftrace supported: YES
|
||||||
|
are kprobes supported: YES
|
||||||
|
timeline data source: FTRACE (all trace events found)
|
||||||
|
is rtcwake supported: YES
|
||||||
|
verifying timeline kprobes work:
|
||||||
|
_cpu_down: YES
|
||||||
|
_cpu_up: YES
|
||||||
|
acpi_pm_finish: YES
|
||||||
|
acpi_pm_prepare: YES
|
||||||
|
freeze_kernel_threads: YES
|
||||||
|
freeze_processes: YES
|
||||||
|
sys_sync: YES
|
||||||
|
thaw_processes: YES
|
||||||
|
verifying dev kprobes work:
|
||||||
|
__const_udelay: YES
|
||||||
|
__mutex_lock_slowpath: YES
|
||||||
|
acpi_os_stall: YES
|
||||||
|
acpi_ps_parse_aml: YES
|
||||||
|
intel_opregion_init: NO
|
||||||
|
intel_opregion_register: NO
|
||||||
|
intel_opregion_setup: NO
|
||||||
|
msleep: YES
|
||||||
|
schedule_timeout: YES
|
||||||
|
schedule_timeout_uninterruptible: YES
|
||||||
|
usleep_range: YES
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
| TESTING ON CONSUMER LINUX OPERATING SYSTEMS |
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
Android
|
||||||
|
_______
|
||||||
|
|
||||||
|
The easiest way to execute on an android device is to run the android.sh
|
||||||
|
script on the device, then pull the ftrace log back to the host and run
|
||||||
|
sleepgraph.py on it.
|
||||||
|
|
||||||
|
Here are the steps:
|
||||||
|
|
||||||
|
[download and install the tool on the device]
|
||||||
|
|
||||||
|
host%> wget https://raw.githubusercontent.com/intel/pm-graph/master/tools/android.sh
|
||||||
|
host%> adb connect 192.168.1.6
|
||||||
|
host%> adb root
|
||||||
|
# push the script to a writeable location
|
||||||
|
host%> adb push android.sh /sdcard/
|
||||||
|
|
||||||
|
[check whether the tool will run on your device]
|
||||||
|
|
||||||
|
host%> adb shell
|
||||||
|
dev%> cd /sdcard
|
||||||
|
dev%> sh android.sh status
|
||||||
|
host : asus_t100
|
||||||
|
kernel : 3.14.0-i386-dirty
|
||||||
|
modes : freeze mem
|
||||||
|
rtcwake : supported
|
||||||
|
ftrace : supported
|
||||||
|
trace events {
|
||||||
|
suspend_resume: found
|
||||||
|
device_pm_callback_end: found
|
||||||
|
device_pm_callback_start: found
|
||||||
|
}
|
||||||
|
# the above is what you see on a system that's properly patched
|
||||||
|
|
||||||
|
[execute the suspend]
|
||||||
|
|
||||||
|
# NOTE: The suspend will only work if the screen isn't timed out,
|
||||||
|
# so you have to press some keys first to wake it up b4 suspend)
|
||||||
|
dev%> sh android.sh suspend mem
|
||||||
|
------------------------------------
|
||||||
|
Suspend/Resume timing test initiated
|
||||||
|
------------------------------------
|
||||||
|
hostname : asus_t100
|
||||||
|
kernel : 3.14.0-i386-dirty
|
||||||
|
mode : mem
|
||||||
|
ftrace out : /mnt/shell/emulated/0/ftrace.txt
|
||||||
|
dmesg out : /mnt/shell/emulated/0/dmesg.txt
|
||||||
|
log file : /mnt/shell/emulated/0/log.txt
|
||||||
|
------------------------------------
|
||||||
|
INITIALIZING FTRACE........DONE
|
||||||
|
STARTING FTRACE
|
||||||
|
SUSPEND START @ 21:24:02 (rtcwake in 10 seconds)
|
||||||
|
<adb connection will now terminate>
|
||||||
|
|
||||||
|
[retrieve the data from the device]
|
||||||
|
|
||||||
|
# I find that you have to actually kill the adb process and
|
||||||
|
# reconnect sometimes in order for the connection to work post-suspend
|
||||||
|
host%> adb connect 192.168.1.6
|
||||||
|
# (required) get the ftrace data, this is the most important piece
|
||||||
|
host%> adb pull /sdcard/ftrace.txt
|
||||||
|
# (optional) get the dmesg data, this is for debugging
|
||||||
|
host%> adb pull /sdcard/dmesg.txt
|
||||||
|
# (optional) get the log, which just lists some test times for comparison
|
||||||
|
host%> adb pull /sdcard/log.txt
|
||||||
|
|
||||||
|
[create an output html file using sleepgraph.py]
|
||||||
|
|
||||||
|
host%> sleepgraph.py -ftrace ftrace.txt
|
||||||
|
|
||||||
|
You should now have an output.html with the android data, enjoy!
|
@ -325,9 +325,9 @@ def parseKernelLog():
|
|||||||
if(not sysvals.stamp['kernel']):
|
if(not sysvals.stamp['kernel']):
|
||||||
sysvals.stamp['kernel'] = sysvals.kernelVersion(msg)
|
sysvals.stamp['kernel'] = sysvals.kernelVersion(msg)
|
||||||
continue
|
continue
|
||||||
m = re.match('.* setting system clock to (?P<t>.*) UTC.*', msg)
|
m = re.match('.* setting system clock to (?P<d>[0-9\-]*)[ A-Z](?P<t>[0-9:]*) UTC.*', msg)
|
||||||
if(m):
|
if(m):
|
||||||
bt = datetime.strptime(m.group('t'), '%Y-%m-%d %H:%M:%S')
|
bt = datetime.strptime(m.group('d')+' '+m.group('t'), '%Y-%m-%d %H:%M:%S')
|
||||||
bt = bt - timedelta(seconds=int(ktime))
|
bt = bt - timedelta(seconds=int(ktime))
|
||||||
data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S')
|
data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S')
|
||||||
sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p')
|
sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p')
|
||||||
@ -348,7 +348,7 @@ def parseKernelLog():
|
|||||||
data.newAction(phase, f, pid, start, ktime, int(r), int(t))
|
data.newAction(phase, f, pid, start, ktime, int(r), int(t))
|
||||||
del devtemp[f]
|
del devtemp[f]
|
||||||
continue
|
continue
|
||||||
if(re.match('^Freeing unused kernel memory.*', msg)):
|
if(re.match('^Freeing unused kernel .*', msg)):
|
||||||
data.tUserMode = ktime
|
data.tUserMode = ktime
|
||||||
data.dmesg['kernel']['end'] = ktime
|
data.dmesg['kernel']['end'] = ktime
|
||||||
data.dmesg['user']['start'] = ktime
|
data.dmesg['user']['start'] = ktime
|
||||||
@ -1008,7 +1008,7 @@ if __name__ == '__main__':
|
|||||||
updateKernelParams()
|
updateKernelParams()
|
||||||
elif cmd == 'flistall':
|
elif cmd == 'flistall':
|
||||||
for f in sysvals.getBootFtraceFilterFunctions():
|
for f in sysvals.getBootFtraceFilterFunctions():
|
||||||
print f
|
print(f)
|
||||||
elif cmd == 'checkbl':
|
elif cmd == 'checkbl':
|
||||||
sysvals.getBootLoader()
|
sysvals.getBootLoader()
|
||||||
pprint('Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec))
|
pprint('Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec))
|
||||||
|
@ -98,12 +98,34 @@ postdelay: 0
|
|||||||
# graph only devices longer than min in the timeline (default: 0.001 ms)
|
# graph only devices longer than min in the timeline (default: 0.001 ms)
|
||||||
mindev: 0.001
|
mindev: 0.001
|
||||||
|
|
||||||
|
# Call Loop Max Gap (dev mode only)
|
||||||
|
# merge loops of the same call if each is less than maxgap apart (def: 100us)
|
||||||
|
callloop-maxgap: 0.0001
|
||||||
|
|
||||||
|
# Call Loop Max Length (dev mode only)
|
||||||
|
# merge loops of the same call if each is less than maxlen in length (def: 5ms)
|
||||||
|
callloop-maxlen: 0.005
|
||||||
|
|
||||||
|
# Override default timeline entries:
|
||||||
|
# Do not use the internal default functions for timeline entries (def: false)
|
||||||
|
# Set this to true if you intend to only use the ones defined in the config
|
||||||
|
override-timeline-functions: true
|
||||||
|
|
||||||
|
# Override default dev timeline entries:
|
||||||
|
# Do not use the internal default functions for dev timeline entries (def: false)
|
||||||
|
# Set this to true if you intend to only use the ones defined in the config
|
||||||
|
override-dev-timeline-functions: true
|
||||||
|
|
||||||
# ---- Debug Options ----
|
# ---- Debug Options ----
|
||||||
|
|
||||||
# Callgraph
|
# Callgraph
|
||||||
# gather detailed ftrace callgraph data on all timeline events (default: false)
|
# gather detailed ftrace callgraph data on all timeline events (default: false)
|
||||||
callgraph: false
|
callgraph: false
|
||||||
|
|
||||||
|
# Max graph depth
|
||||||
|
# limit the callgraph trace to this depth (default: 0 = all)
|
||||||
|
maxdepth: 2
|
||||||
|
|
||||||
# Callgraph phase filter
|
# Callgraph phase filter
|
||||||
# Only enable callgraphs for one phase, i.e. resume_noirq (default: all)
|
# Only enable callgraphs for one phase, i.e. resume_noirq (default: all)
|
||||||
cgphase: suspend
|
cgphase: suspend
|
||||||
@ -131,3 +153,7 @@ timeprec: 6
|
|||||||
# Add kprobe functions to the timeline
|
# Add kprobe functions to the timeline
|
||||||
# Add functions to the timeline from a text file (default: no-action)
|
# Add functions to the timeline from a text file (default: no-action)
|
||||||
# fadd: file.txt
|
# fadd: file.txt
|
||||||
|
|
||||||
|
# Ftrace buffer size
|
||||||
|
# Set trace buffer size to N kilo-bytes (default: all of free memory up to 3GB)
|
||||||
|
# bufsize: 1000
|
||||||
|
@ -53,6 +53,11 @@ disable rtcwake and require a user keypress to resume.
|
|||||||
Add the dmesg and ftrace logs to the html output. They will be viewable by
|
Add the dmesg and ftrace logs to the html output. They will be viewable by
|
||||||
clicking buttons in the timeline.
|
clicking buttons in the timeline.
|
||||||
.TP
|
.TP
|
||||||
|
\fB-turbostat\fR
|
||||||
|
Use turbostat to execute the command in freeze mode (default: disabled). This
|
||||||
|
will provide turbostat output in the log which will tell you which actual
|
||||||
|
power modes were entered.
|
||||||
|
.TP
|
||||||
\fB-result \fIfile\fR
|
\fB-result \fIfile\fR
|
||||||
Export a results table to a text file for parsing.
|
Export a results table to a text file for parsing.
|
||||||
.TP
|
.TP
|
||||||
@ -121,6 +126,10 @@ be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}.
|
|||||||
Use ftrace to create device callgraphs (default: disabled). This can produce
|
Use ftrace to create device callgraphs (default: disabled). This can produce
|
||||||
very large outputs, i.e. 10MB - 100MB.
|
very large outputs, i.e. 10MB - 100MB.
|
||||||
.TP
|
.TP
|
||||||
|
\fB-ftop\fR
|
||||||
|
Use ftrace on the top level call: "suspend_devices_and_enter" only (default: disabled).
|
||||||
|
This option implies -f and creates a single callgraph covering all of suspend/resume.
|
||||||
|
.TP
|
||||||
\fB-maxdepth \fIlevel\fR
|
\fB-maxdepth \fIlevel\fR
|
||||||
limit the callgraph trace depth to \fIlevel\fR (default: 0=all). This is
|
limit the callgraph trace depth to \fIlevel\fR (default: 0=all). This is
|
||||||
the best way to limit the output size when using callgraphs via -f.
|
the best way to limit the output size when using callgraphs via -f.
|
||||||
@ -138,8 +147,8 @@ which are barely visible in the timeline.
|
|||||||
The value is a float: e.g. 0.001 represents 1 us.
|
The value is a float: e.g. 0.001 represents 1 us.
|
||||||
.TP
|
.TP
|
||||||
\fB-cgfilter \fI"func1,func2,..."\fR
|
\fB-cgfilter \fI"func1,func2,..."\fR
|
||||||
Reduce callgraph output in the timeline by limiting it to a list of calls. The
|
Reduce callgraph output in the timeline by limiting it certain devices. The
|
||||||
argument can be a single function name or a comma delimited list.
|
argument can be a single device name or a comma delimited list.
|
||||||
(default: none)
|
(default: none)
|
||||||
.TP
|
.TP
|
||||||
\fB-cgskip \fIfile\fR
|
\fB-cgskip \fIfile\fR
|
||||||
@ -183,6 +192,9 @@ Print out the contents of the ACPI Firmware Performance Data Table.
|
|||||||
\fB-battery\fR
|
\fB-battery\fR
|
||||||
Print out battery status and current charge.
|
Print out battery status and current charge.
|
||||||
.TP
|
.TP
|
||||||
|
\fB-wifi\fR
|
||||||
|
Print out wifi status and connection details.
|
||||||
|
.TP
|
||||||
\fB-xon/-xoff/-xstandby/-xsuspend\fR
|
\fB-xon/-xoff/-xstandby/-xsuspend\fR
|
||||||
Test xset by attempting to switch the display to the given mode. This
|
Test xset by attempting to switch the display to the given mode. This
|
||||||
is the same command which will be issued by \fB-display \fImode\fR.
|
is the same command which will be issued by \fB-display \fImode\fR.
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user