Skip to content

Commit

Permalink
PM / devfreq: Add governor attribute flag for specifc sysfs nodes
Browse files Browse the repository at this point in the history
DEVFREQ supports the default governors like performance, simple_ondemand and
also allows the devfreq driver to add their own governor like tegra30-devfreq.c
according to their requirement. In result, some sysfs attributes are useful
or not useful. Prior to that the user can access all sysfs attributes
regardless of the available attributes.

So, clarify the access permission of sysfs attributes according to governor.
When adding the devfreq governor, can specify the available attribute
information by using DEVFREQ_GOV_ATTR_* constant variable. The user can
read or write the sysfs attributes in accordance to the specified attributes.

When adding the governor, can add the following attributes
according to the governor feature.

[Definition for speific sysfs attributes]
- DEVFREQ_GOV_ATTR_POLLING_INTERVAL to update polling interval for timer.
  : /sys/class/devfreq/[devfreq dev name]/polling_interval
- DEVFREQ_GOV_ATTR_TIMER to change the type of timer on either deferrable
  or dealyed timer.
  : /sys/class/devfreq/[devfreq dev name]/timer

And all devfreq governors have to support the following common attributes.
The common attributes are added to devfreq class by default.
- governor
- available_governors
- available_frequencies
- cur_freq
- target_freq
- min_freq
- max_freq
- trans_stat

[Table of governor attribute flags for devfreq governors]
------------------------------------------------------------------------------
                      | simple    | perfor | power | user | passive | tegra30
		      | ondemand  | mance  | save  | space|         |
------------------------------------------------------------------------------
governor              | O         | O      | O     | O    | O       | O
available_governors   | O         | O      | O     | O    | O       | O
available_frequencies | O         | O      | O     | O    | O       | O
cur_freq              | O         | O      | O     | O    | O       | O
target_freq           | O         | O      | O     | O    | O       | O
min_freq              | O         | O      | O     | O    | O       | O
max_freq              | O         | O      | O     | O    | O       | O
trans_stat            | O         | O      | O     | O    | O       | O
                      --------------------------------------------------------
polling_interval      | O         | X      | X     | X    | X       | O
timer                 | O         | X      | X     | X    | X       | X
------------------------------------------------------------------------------

Reviewed-by: Dmitry Osipenko <[email protected]>
Tested-by: Dmitry Osipenko <[email protected]>
Signed-off-by: Chanwoo Choi <[email protected]>
  • Loading branch information
chanwoochoi committed Oct 26, 2020
1 parent 0dd25a0 commit 5f1a906
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 72 deletions.
54 changes: 32 additions & 22 deletions Documentation/ABI/testing/sysfs-class-devfreq
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,6 @@ Description:
The /sys/class/devfreq/.../target_freq shows the next governor
predicted target frequency of the corresponding devfreq object.

What: /sys/class/devfreq/.../polling_interval
Date: September 2011
Contact: MyungJoo Ham <[email protected]>
Description:
The /sys/class/devfreq/.../polling_interval shows and sets
the requested polling interval of the corresponding devfreq
object. The values are represented in ms. If the value is
less than 1 jiffy, it is considered to be 0, which means
no polling. This value is meaningless if the governor is
not polling; thus. If the governor is not using
devfreq-provided central polling
(/sys/class/devfreq/.../central_polling is 0), this value
may be useless.

What: /sys/class/devfreq/.../trans_stat
Date: October 2012
Contact: MyungJoo Ham <[email protected]>
Expand All @@ -65,14 +51,6 @@ Description:
as following:
echo 0 > /sys/class/devfreq/.../trans_stat

What: /sys/class/devfreq/.../userspace/set_freq
Date: September 2011
Contact: MyungJoo Ham <[email protected]>
Description:
The /sys/class/devfreq/.../userspace/set_freq shows and
sets the requested frequency for the devfreq object if
userspace governor is in effect.

What: /sys/class/devfreq/.../available_frequencies
Date: October 2012
Contact: Nishanth Menon <[email protected]>
Expand Down Expand Up @@ -109,6 +87,35 @@ Description:
The max_freq overrides min_freq because max_freq may be
used to throttle devices to avoid overheating.

What: /sys/class/devfreq/.../polling_interval
Date: September 2011
Contact: MyungJoo Ham <[email protected]>
Description:
The /sys/class/devfreq/.../polling_interval shows and sets
the requested polling interval of the corresponding devfreq
object. The values are represented in ms. If the value is
less than 1 jiffy, it is considered to be 0, which means
no polling. This value is meaningless if the governor is
not polling; thus. If the governor is not using
devfreq-provided central polling
(/sys/class/devfreq/.../central_polling is 0), this value
may be useless.

A list of governors that support the node:
- simple_ondmenad
- tegra_actmon

What: /sys/class/devfreq/.../userspace/set_freq
Date: September 2011
Contact: MyungJoo Ham <[email protected]>
Description:
The /sys/class/devfreq/.../userspace/set_freq shows and
sets the requested frequency for the devfreq object if
userspace governor is in effect.

A list of governors that support the node:
- userspace

What: /sys/class/devfreq/.../timer
Date: July 2020
Contact: Chanwoo Choi <[email protected]>
Expand All @@ -120,3 +127,6 @@ Description:
as following:
echo deferrable > /sys/class/devfreq/.../timer
echo delayed > /sys/class/devfreq/.../timer

A list of governors that support the node:
- simple_ondemand
162 changes: 112 additions & 50 deletions drivers/devfreq/devfreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <trace/events/devfreq.h>

#define IS_SUPPORTED_FLAG(f, name) ((f & DEVFREQ_GOV_FLAG_##name) ? true : false)
#define IS_SUPPORTED_ATTR(f, name) ((f & DEVFREQ_GOV_ATTR_##name) ? true : false)
#define HZ_PER_KHZ 1000

static struct class *devfreq_class;
Expand Down Expand Up @@ -760,6 +761,11 @@ static void devfreq_dev_release(struct device *dev)
kfree(devfreq);
}

static void create_sysfs_files(struct devfreq *devfreq,
const struct devfreq_governor *gov);
static void remove_sysfs_files(struct devfreq *devfreq,
const struct devfreq_governor *gov);

/**
* devfreq_add_device() - Add devfreq feature to the device
* @dev: the device to add devfreq feature.
Expand Down Expand Up @@ -917,6 +923,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
__func__);
goto err_init;
}
create_sysfs_files(devfreq, devfreq->governor);

list_add(&devfreq->node, &devfreq_list);

Expand Down Expand Up @@ -947,9 +954,12 @@ int devfreq_remove_device(struct devfreq *devfreq)
if (!devfreq)
return -EINVAL;

if (devfreq->governor)
if (devfreq->governor) {
devfreq->governor->event_handler(devfreq,
DEVFREQ_GOV_STOP, NULL);
remove_sysfs_files(devfreq, devfreq->governor);
}

device_unregister(&devfreq->dev);

return 0;
Expand Down Expand Up @@ -1378,20 +1388,31 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
goto out;
}

/*
* Stop the current governor and remove the specific sysfs files
* which depend on current governor.
*/
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
if (ret) {
dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
__func__, df->governor->name, ret);
goto out;
}
remove_sysfs_files(df, df->governor);

/*
* Start the new governor and create the specific sysfs files
* which depend on the new governor.
*/
prev_governor = df->governor;
df->governor = governor;
strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
if (ret) {
dev_warn(dev, "%s: Governor %s not started(%d)\n",
__func__, df->governor->name, ret);

/* Restore previous governor */
df->governor = prev_governor;
strncpy(df->governor_name, prev_governor->name,
DEVFREQ_NAME_LEN);
Expand All @@ -1401,8 +1422,16 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
"%s: reverting to Governor %s failed (%d)\n",
__func__, df->governor_name, ret);
df->governor = NULL;
goto out;
}
}

/*
* Create the sysfs files for the new governor. But if failed to start
* the new governor, restore the sysfs files of previous governor.
*/
create_sysfs_files(df, df->governor);

out:
mutex_unlock(&devfreq_list_lock);

Expand Down Expand Up @@ -1484,39 +1513,6 @@ static ssize_t target_freq_show(struct device *dev,
}
static DEVICE_ATTR_RO(target_freq);

static ssize_t polling_interval_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct devfreq *df = to_devfreq(dev);

if (!df->profile)
return -EINVAL;

return sprintf(buf, "%d\n", df->profile->polling_ms);
}

static ssize_t polling_interval_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct devfreq *df = to_devfreq(dev);
unsigned int value;
int ret;

if (!df->governor)
return -EINVAL;

ret = sscanf(buf, "%u", &value);
if (ret != 1)
return -EINVAL;

df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value);
ret = count;

return ret;
}
static DEVICE_ATTR_RW(polling_interval);

static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
Expand Down Expand Up @@ -1724,6 +1720,53 @@ static ssize_t trans_stat_store(struct device *dev,
}
static DEVICE_ATTR_RW(trans_stat);

static struct attribute *devfreq_attrs[] = {
&dev_attr_name.attr,
&dev_attr_governor.attr,
&dev_attr_available_governors.attr,
&dev_attr_cur_freq.attr,
&dev_attr_available_frequencies.attr,
&dev_attr_target_freq.attr,
&dev_attr_min_freq.attr,
&dev_attr_max_freq.attr,
&dev_attr_trans_stat.attr,
NULL,
};
ATTRIBUTE_GROUPS(devfreq);

static ssize_t polling_interval_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct devfreq *df = to_devfreq(dev);

if (!df->profile)
return -EINVAL;

return sprintf(buf, "%d\n", df->profile->polling_ms);
}

static ssize_t polling_interval_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct devfreq *df = to_devfreq(dev);
unsigned int value;
int ret;

if (!df->governor)
return -EINVAL;

ret = sscanf(buf, "%u", &value);
if (ret != 1)
return -EINVAL;

df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value);
ret = count;

return ret;
}
static DEVICE_ATTR_RW(polling_interval);

static ssize_t timer_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
Expand Down Expand Up @@ -1787,21 +1830,36 @@ static ssize_t timer_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RW(timer);

static struct attribute *devfreq_attrs[] = {
&dev_attr_name.attr,
&dev_attr_governor.attr,
&dev_attr_available_governors.attr,
&dev_attr_cur_freq.attr,
&dev_attr_available_frequencies.attr,
&dev_attr_target_freq.attr,
&dev_attr_polling_interval.attr,
&dev_attr_min_freq.attr,
&dev_attr_max_freq.attr,
&dev_attr_trans_stat.attr,
&dev_attr_timer.attr,
NULL,
};
ATTRIBUTE_GROUPS(devfreq);
#define CREATE_SYSFS_FILE(df, name) \
{ \
int ret; \
ret = sysfs_create_file(&df->dev.kobj, &dev_attr_##name.attr); \
if (ret < 0) { \
dev_warn(&df->dev, \
"Unable to create attr(%s)\n", "##name"); \
} \
} \

/* Create the specific sysfs files which depend on each governor. */
static void create_sysfs_files(struct devfreq *devfreq,
const struct devfreq_governor *gov)
{
if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL))
CREATE_SYSFS_FILE(devfreq, polling_interval);
if (IS_SUPPORTED_ATTR(gov->attrs, TIMER))
CREATE_SYSFS_FILE(devfreq, timer);
}

/* Remove the specific sysfs files which depend on each governor. */
static void remove_sysfs_files(struct devfreq *devfreq,
const struct devfreq_governor *gov)
{
if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL))
sysfs_remove_file(&devfreq->dev.kobj,
&dev_attr_polling_interval.attr);
if (IS_SUPPORTED_ATTR(gov->attrs, TIMER))
sysfs_remove_file(&devfreq->dev.kobj, &dev_attr_timer.attr);
}

/**
* devfreq_summary_show() - Show the summary of the devfreq devices
Expand Down Expand Up @@ -1858,8 +1916,12 @@ static int devfreq_summary_show(struct seq_file *s, void *data)
mutex_lock(&devfreq->lock);
cur_freq = devfreq->previous_freq;
get_freq_range(devfreq, &min_freq, &max_freq);
polling_ms = devfreq->profile->polling_ms;
timer = devfreq->profile->timer;

if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, POLLING_INTERVAL))
polling_ms = devfreq->profile->polling_ms;
else
polling_ms = 0;
mutex_unlock(&devfreq->lock);

seq_printf(s,
Expand Down
12 changes: 12 additions & 0 deletions drivers/devfreq/governor.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,21 @@
#define DEVFREQ_GOV_FLAG_IMMUTABLE BIT(0)
#define DEVFREQ_GOV_FLAG_IRQ_DRIVEN BIT(1)

/*
* Definition of governor attribute flags except for common sysfs attributes
* - DEVFREQ_GOV_ATTR_POLLING_INTERVAL
* : Indicate polling_interal sysfs attribute
* - DEVFREQ_GOV_ATTR_TIMER
* : Indicate timer sysfs attribute
*/
#define DEVFREQ_GOV_ATTR_POLLING_INTERVAL BIT(0)
#define DEVFREQ_GOV_ATTR_TIMER BIT(1)

/**
* struct devfreq_governor - Devfreq policy governor
* @node: list node - contains registered devfreq governors
* @name: Governor's name
* @attrs: Governor's sysfs attribute flags
* @flags: Governor's feature flags
* @get_target_freq: Returns desired operating frequency for the device.
* Basically, get_target_freq will run
Expand All @@ -57,6 +68,7 @@ struct devfreq_governor {
struct list_head node;

const char name[DEVFREQ_NAME_LEN];
const u64 attrs;
const u64 flags;
int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
int (*event_handler)(struct devfreq *devfreq,
Expand Down
2 changes: 2 additions & 0 deletions drivers/devfreq/governor_simpleondemand.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,

static struct devfreq_governor devfreq_simple_ondemand = {
.name = DEVFREQ_GOV_SIMPLE_ONDEMAND,
.attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL
| DEVFREQ_GOV_ATTR_TIMER,
.get_target_freq = devfreq_simple_ondemand_func,
.event_handler = devfreq_simple_ondemand_handler,
};
Expand Down
1 change: 1 addition & 0 deletions drivers/devfreq/tegra30-devfreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,7 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,

static struct devfreq_governor tegra_devfreq_governor = {
.name = "tegra_actmon",
.attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL,
.flags = DEVFREQ_GOV_FLAG_IMMUTABLE
| DEVFREQ_GOV_FLAG_IRQ_DRIVEN,
.get_target_freq = tegra_governor_get_target,
Expand Down

0 comments on commit 5f1a906

Please sign in to comment.