Re: [PATCH v6 4/4] PM / DEVFREQ: add sysfs interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Aug 19, 2011 at 12:28 AM, MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx> wrote:
> On Fri, Aug 19, 2011 at 12:15 PM, Turquette, Mike <mturquette@xxxxxx> wrote:
>> On Thu, Aug 18, 2011 at 3:27 AM, MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx> wrote:
>>> Device specific sysfs interface /sys/devices/.../power/devfreq_*
>>> - governor      R: name of governor
>>> - cur_freq      R: current frequency
>>> - max_freq      R: maximum operable frequency
>>> - min_freq      R: minimum operable frequency
>>> - set_freq      R: read user specified frequency (0 if not specified yet)
>>>                W: set user specified frequency
>>> - polling_interval      R: polling interval in ms given with devfreq profile
>>
>> This code still does not export up_threshold/down_threshold entries
>> for simple-ondemand which I requested in V5.  However there is a much
>> bigger issue here brought out in patch 3.  Those values shouldn't be
>> global, since devices might want different thresholds.
>>
>> The solution is not to put those tunables in struct devfreq.
>> up_threshold and down_threshold must be parameters of the governor,
>> and there must be a per-device instance of the governor.
>>
>> Regards,
>> Mike
>
> As in the example in the reply to the thread of patch 3/4 of v6
> patchset, we do not need to create another per-device object in
> DEVFREQ in order to allow governors to be tuned per device. The global
> values you've mentioned are only used as default and each instance of
> devfreq (per device) can specify its own tuneable values and they can
> alter the values in run-time.
>
> For the sysfs interfaces of governor-specific tunable values, I'd like
> to see that as a "future work" and that would be implemented
> per-governor basis; i.e., probably under
> /sysfs/devices/.../power/NAME_OF_GOVERNOR/variable_name. Or should I
> regard the sysfs interface to tune governors at userland is somewhat
> essential and critical for the initial release?

I guess it is not critical but I feel that implementing all of the
sysfs bits will reveal some designs issues.

E.g. by pushing threshold data into the device's private_data you're
crossing data paths with the governors: the private data tunable must
be associated per-device in sysfs, but the read/write functions must
come from the governor.  If the core code assumes too much about what
private data looks like (up_threshold, down_threshold, etc) then it
might be limiting to future governor authors.

The patches are shaping up however and I won't consider sysfs support
a blocker.  Rafael's point about revisiting design at a later date is
good guidance here.

Regards,
Mike

> Cheers!
>
> MyungJoo
>
>>
>>>
>>> Signed-off-by: MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx>
>>> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
>>>
>>> --
>>> Changed from v5
>>> - updated devferq_update usage.
>>>
>>> Changed from v4
>>> - removed system-wide sysfs interface
>>> - removed tickling sysfs interface
>>> - added set_freq for userspace governor (and any other governors that
>>>  require user input)
>>>
>>> Changed from v3
>>> - corrected sysfs API usage
>>> - corrected error messages
>>> - moved sysfs entry location
>>> - added sysfs entries
>>>
>>> Changed from v2
>>> - add ABI entries for devfreq sysfs interface
>>> ---
>>>  Documentation/ABI/testing/sysfs-devices-power |   45 +++++++
>>>  drivers/devfreq/devfreq.c                     |  152 +++++++++++++++++++++++++
>>>  2 files changed, 197 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power
>>> index 8ffbc25..6e77604 100644
>>> --- a/Documentation/ABI/testing/sysfs-devices-power
>>> +++ b/Documentation/ABI/testing/sysfs-devices-power
>>> @@ -165,3 +165,48 @@ Description:
>>>
>>>                Not all drivers support this attribute.  If it isn't supported,
>>>                attempts to read or write it will yield I/O errors.
>>> +
>>> +What:          /sys/devices/.../power/devfreq_governor
>>> +Date:          July 2011
>>> +Contact:       MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx>
>>> +Description:
>>> +               The /sys/devices/.../power/devfreq_governor shows the name
>>> +               of the governor used by the corresponding device.
>>> +
>>> +What:          /sys/devices/.../power/devfreq_cur_freq
>>> +Date:          July 2011
>>> +Contact:       MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx>
>>> +Description:
>>> +               The /sys/devices/.../power/devfreq_cur_freq shows the current
>>> +               frequency of the corresponding device.
>>> +
>>> +What:          /sys/devices/.../power/devfreq_max_freq
>>> +Date:          July 2011
>>> +Contact:       MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx>
>>> +Description:
>>> +               The /sys/devices/.../power/devfreq_cur_freq shows the
>>> +               maximum operable frequency of the corresponding device.
>>> +
>>> +What:          /sys/devices/.../power/devfreq_min_freq
>>> +Date:          July 2011
>>> +Contact:       MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx>
>>> +Description:
>>> +               The /sys/devices/.../power/devfreq_cur_freq shows the
>>> +               minimum operable frequency of the corresponding device.
>>> +
>>> +What:          /sys/devices/.../power/devfreq_set_freq
>>> +Date:          August 2011
>>> +Contact:       MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx>
>>> +Description:
>>> +               The /sys/devices/.../power/devfreq_set_freq sets and shows
>>> +               the user specified desired frequency of the device. The
>>> +               governor may and may not use the value. With the basic
>>> +               governors given with devfreq.c, userspace governor is
>>> +               using the value.
>>> +
>>> +What:          /sys/devices/.../power/devfreq_polling_interval
>>> +Date:          July 2011
>>> +Contact:       MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx>
>>> +Description:
>>> +               The /sys/devices/.../power/devfreq_polling_interval shows the
>>> +               requested polling interval of the corresponding device.
>>> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
>>> index 2036f2c..13b422f 100644
>>> --- a/drivers/devfreq/devfreq.c
>>> +++ b/drivers/devfreq/devfreq.c
>>> @@ -37,6 +37,8 @@ static struct delayed_work devfreq_work;
>>>  static LIST_HEAD(devfreq_list);
>>>  static DEFINE_MUTEX(devfreq_list_lock);
>>>
>>> +static struct attribute_group dev_attr_group;
>>> +
>>>  /**
>>>  * find_device_devfreq() - find devfreq struct using device pointer
>>>  * @dev:       device pointer used to lookup device devfreq.
>>> @@ -154,6 +156,8 @@ static void devfreq_monitor(struct work_struct *work)
>>>                                dev_err(devfreq->dev, "Due to devfreq_do error(%d), devfreq(%s) is removed from the device\n",
>>>                                        error, devfreq->governor->name);
>>>
>>> +                               sysfs_unmerge_group(&devfreq->dev->kobj,
>>> +                                                   &dev_attr_group);
>>>                                list_del(&devfreq->node);
>>>                                kfree(devfreq);
>>>
>>> @@ -244,6 +248,8 @@ int devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile,
>>>                queue_delayed_work(devfreq_wq, &devfreq_work,
>>>                                   devfreq->next_polling);
>>>        }
>>> +
>>> +       sysfs_merge_group(&dev->kobj, &dev_attr_group);
>>>  out:
>>>        mutex_unlock(&devfreq_list_lock);
>>>
>>> @@ -276,6 +282,8 @@ int devfreq_remove_device(struct device *dev)
>>>                goto out;
>>>        }
>>>
>>> +       sysfs_unmerge_group(&dev->kobj, &dev_attr_group);
>>> +
>>>        list_del(&devfreq->node);
>>>        srcu_notifier_chain_unregister(nh, &devfreq->nb);
>>>        kfree(devfreq);
>>> @@ -284,6 +292,150 @@ out:
>>>        return 0;
>>>  }
>>>
>>> +static ssize_t show_governor(struct device *dev,
>>> +                            struct device_attribute *attr, char *buf)
>>> +{
>>> +       struct devfreq *df = find_device_devfreq(dev);
>>> +
>>> +       if (IS_ERR(df))
>>> +               return PTR_ERR(df);
>>> +       if (!df->governor)
>>> +               return -EINVAL;
>>> +
>>> +       return sprintf(buf, "%s\n", df->governor->name);
>>> +}
>>> +
>>> +static ssize_t show_freq(struct device *dev,
>>> +                        struct device_attribute *attr, char *buf)
>>> +{
>>> +       struct devfreq *df = find_device_devfreq(dev);
>>> +
>>> +       if (IS_ERR(df))
>>> +               return PTR_ERR(df);
>>> +
>>> +       return sprintf(buf, "%lu\n", df->previous_freq);
>>> +}
>>> +
>>> +static ssize_t show_max_freq(struct device *dev,
>>> +                            struct device_attribute *attr, char *buf)
>>> +{
>>> +       struct devfreq *df = find_device_devfreq(dev);
>>> +       unsigned long freq = ULONG_MAX;
>>> +       struct opp *opp;
>>> +
>>> +       if (IS_ERR(df))
>>> +               return PTR_ERR(df);
>>> +       if (!df->dev)
>>> +               return -EINVAL;
>>> +
>>> +       opp = opp_find_freq_floor(df->dev, &freq);
>>> +       if (IS_ERR(opp))
>>> +               return PTR_ERR(opp);
>>> +
>>> +       return sprintf(buf, "%lu\n", freq);
>>> +}
>>> +
>>> +static ssize_t show_min_freq(struct device *dev,
>>> +                            struct device_attribute *attr, char *buf)
>>> +{
>>> +       struct devfreq *df = find_device_devfreq(dev);
>>> +       unsigned long freq = 0;
>>> +       struct opp *opp;
>>> +
>>> +       if (IS_ERR(df))
>>> +               return PTR_ERR(df);
>>> +       if (!df->dev)
>>> +               return -EINVAL;
>>> +
>>> +       opp = opp_find_freq_ceil(df->dev, &freq);
>>> +       if (IS_ERR(opp))
>>> +               return PTR_ERR(opp);
>>> +
>>> +       return sprintf(buf, "%lu\n", freq);
>>> +}
>>> +
>>> +static ssize_t show_polling_interval(struct device *dev,
>>> +                                    struct device_attribute *attr, char *buf)
>>> +{
>>> +       struct devfreq *df = find_device_devfreq(dev);
>>> +
>>> +       if (IS_ERR(df))
>>> +               return PTR_ERR(df);
>>> +       if (!df->profile)
>>> +               return -EINVAL;
>>> +
>>> +       return sprintf(buf, "%d\n", df->profile->polling_ms);
>>> +}
>>> +
>>> +static ssize_t set_user_frequency(struct device *dev,
>>> +                                 struct device_attribute *attr,
>>> +                                 const char *buf, size_t count)
>>> +{
>>> +       struct devfreq *devfreq;
>>> +       unsigned long wanted;
>>> +       int err = 0;
>>> +
>>> +       sscanf(buf, "%lu", &wanted);
>>> +
>>> +       mutex_lock(&devfreq_list_lock);
>>> +       devfreq = find_device_devfreq(dev);
>>> +
>>> +       if (IS_ERR(devfreq)) {
>>> +               err = PTR_ERR(devfreq);
>>> +               goto out;
>>> +       }
>>> +
>>> +       devfreq->user_set_freq = wanted;
>>> +       err = count;
>>> +out:
>>> +       mutex_unlock(&devfreq_list_lock);
>>> +       if (err >= 0)
>>> +               devfreq_update(&devfreq->nb, 0, NULL);
>>> +       return err;
>>> +}
>>> +
>>> +static ssize_t show_user_frequency(struct device *dev,
>>> +                                  struct device_attribute *attr, char *buf)
>>> +{
>>> +       struct devfreq *devfreq;
>>> +       int err = 0;
>>> +
>>> +       mutex_lock(&devfreq_list_lock);
>>> +       devfreq = find_device_devfreq(dev);
>>> +
>>> +       if (IS_ERR(devfreq)) {
>>> +               err = PTR_ERR(devfreq);
>>> +               goto out;
>>> +       }
>>> +
>>> +       err = sprintf(buf, "%lu\n", devfreq->user_set_freq);
>>> +out:
>>> +       mutex_unlock(&devfreq_list_lock);
>>> +       return err;
>>> +
>>> +}
>>> +
>>> +static DEVICE_ATTR(devfreq_governor, 0444, show_governor, NULL);
>>> +static DEVICE_ATTR(devfreq_cur_freq, 0444, show_freq, NULL);
>>> +static DEVICE_ATTR(devfreq_max_freq, 0444, show_max_freq, NULL);
>>> +static DEVICE_ATTR(devfreq_min_freq, 0444, show_min_freq, NULL);
>>> +static DEVICE_ATTR(devfreq_polling_interval, 0444, show_polling_interval, NULL);
>>> +static DEVICE_ATTR(devfreq_set_freq, 0644, show_user_frequency,
>>> +                  set_user_frequency);
>>> +static struct attribute *dev_entries[] = {
>>> +       &dev_attr_devfreq_governor.attr,
>>> +       &dev_attr_devfreq_cur_freq.attr,
>>> +       &dev_attr_devfreq_max_freq.attr,
>>> +       &dev_attr_devfreq_min_freq.attr,
>>> +       &dev_attr_devfreq_polling_interval.attr,
>>> +       &dev_attr_devfreq_set_freq.attr,
>>> +       NULL,
>>> +};
>>> +static struct attribute_group dev_attr_group = {
>>> +       .name   = power_group_name,
>>> +       .attrs  = dev_entries,
>>> +};
>>> +
>>>  /**
>>>  * devfreq_init() - Initialize data structure for devfreq framework and
>>>  *               start polling registered devfreq devices.
>>> --
>>> 1.7.4.1
>>>
>>>
>>
>
>
>
> --
> MyungJoo Ham (함명주), Ph.D.
> Mobile Software Platform Lab,
> Digital Media and Communications (DMC) Business
> Samsung Electronics
> cell: 82-10-6714-2858
>
_______________________________________________
linux-pm mailing list
linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linux-foundation.org/mailman/listinfo/linux-pm



[Index of Archives]     [Linux ACPI]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [CPU Freq]     [Kernel Newbies]     [Fedora Kernel]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux