diff options
| author | Pengjie Zhang <zhangpengjie2@huawei.com> | 2025-12-16 11:11:53 +0800 |
|---|---|---|
| committer | Chanwoo Choi <cw00.choi@samsung.com> | 2026-04-04 03:15:38 +0900 |
| commit | 943a872fe41a8352d64b20de77d8b707978e5732 (patch) | |
| tree | 37504cbc8d1ebbcca5358c21208be16ed20063a4 /drivers/devfreq | |
| parent | 82bb8dc953c2203bb710b7358f030b1d9bdf698d (diff) | |
PM / devfreq: use _visible attribute to replace create/remove_sysfs_files()
Previously, non-generic attributes (polling_interval, timer) used separate
create/delete logic, leading to race conditions during concurrent access in
creation/deletion. Multi-threaded operations also caused inconsistencies
between governor capabilities and attribute states.
1.Use is_visible + sysfs_update_group() to unify management of these
attributes, eliminating creation/deletion races.
2.Add locks and validation to these attributes, ensuring consistency
between current governor capabilities and attribute operations in
multi-threaded environments.
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Jie Zhan <zhanjie9@hisilicon.com>
Signed-off-by: Pengjie Zhang <zhangpengjie2@huawei.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Link: https://www.spinics.net/lists/kernel/msg5967745.html
Diffstat (limited to 'drivers/devfreq')
| -rw-r--r-- | drivers/devfreq/devfreq.c | 103 |
1 files changed, 60 insertions, 43 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 54f0b18536db..82dd9a43dc62 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -38,6 +38,7 @@ static struct class *devfreq_class; static struct dentry *devfreq_debugfs; +static const struct attribute_group gov_attr_group; /* * devfreq core provides delayed work based load monitoring helper @@ -784,11 +785,6 @@ 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. @@ -955,7 +951,10 @@ struct devfreq *devfreq_add_device(struct device *dev, __func__); goto err_init; } - create_sysfs_files(devfreq, devfreq->governor); + + err = sysfs_update_group(&devfreq->dev.kobj, &gov_attr_group); + if (err) + goto err_init; list_add(&devfreq->node, &devfreq_list); @@ -994,12 +993,9 @@ int devfreq_remove_device(struct devfreq *devfreq) devfreq_cooling_unregister(devfreq->cdev); - 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; @@ -1459,7 +1455,6 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, __func__, df->governor->name, ret); goto out; } - remove_sysfs_files(df, df->governor); /* * Start the new governor and create the specific sysfs files @@ -1488,7 +1483,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, * 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); + ret = sysfs_update_group(&df->dev.kobj, &gov_attr_group); out: mutex_unlock(&devfreq_list_lock); @@ -1806,14 +1801,17 @@ static struct attribute *devfreq_attrs[] = { &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) + /* Protect against race between sysfs attrs update and read/write */ + guard(mutex)(&devfreq_list_lock); + + if (!df->profile || !df->governor || + !IS_SUPPORTED_ATTR(df->governor->attrs, POLLING_INTERVAL)) return -EINVAL; return sprintf(buf, "%d\n", df->profile->polling_ms); @@ -1827,7 +1825,10 @@ static ssize_t polling_interval_store(struct device *dev, unsigned int value; int ret; - if (!df->governor) + guard(mutex)(&devfreq_list_lock); + + if (!df->governor || + !IS_SUPPORTED_ATTR(df->governor->attrs, POLLING_INTERVAL)) return -EINVAL; ret = sscanf(buf, "%u", &value); @@ -1846,7 +1847,10 @@ static ssize_t timer_show(struct device *dev, { struct devfreq *df = to_devfreq(dev); - if (!df->profile) + guard(mutex)(&devfreq_list_lock); + + if (!df->profile || !df->governor || + !IS_SUPPORTED_ATTR(df->governor->attrs, TIMER)) return -EINVAL; return sprintf(buf, "%s\n", timer_name[df->profile->timer]); @@ -1860,7 +1864,10 @@ static ssize_t timer_store(struct device *dev, struct device_attribute *attr, int timer = -1; int ret = 0, i; - if (!df->governor || !df->profile) + guard(mutex)(&devfreq_list_lock); + + if (!df->governor || !df->profile || + !IS_SUPPORTED_ATTR(df->governor->attrs, TIMER)) return -EINVAL; ret = sscanf(buf, "%16s", str_timer); @@ -1904,37 +1911,47 @@ out: } static DEVICE_ATTR_RW(timer); -#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"); \ - } \ -} \ +static struct attribute *governor_attrs[] = { + &dev_attr_polling_interval.attr, + &dev_attr_timer.attr, + NULL +}; -/* Create the specific sysfs files which depend on each governor. */ -static void create_sysfs_files(struct devfreq *devfreq, - const struct devfreq_governor *gov) +static umode_t gov_attr_visible(struct kobject *kobj, + struct attribute *attr, int n) { - 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); -} + struct device *dev = kobj_to_dev(kobj); + struct devfreq *df = to_devfreq(dev); -/* 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); + if (!df->governor || !df->governor->attrs) + return 0; + + if (attr == &dev_attr_polling_interval.attr && + IS_SUPPORTED_ATTR(df->governor->attrs, POLLING_INTERVAL)) + return attr->mode; + + if (attr == &dev_attr_timer.attr && + IS_SUPPORTED_ATTR(df->governor->attrs, TIMER)) + return attr->mode; + + return 0; } +static const struct attribute_group devfreq_group = { + .attrs = devfreq_attrs, +}; + +static const struct attribute_group gov_attr_group = { + .attrs = governor_attrs, + .is_visible = gov_attr_visible, +}; + +static const struct attribute_group *devfreq_groups[] = { + &devfreq_group, + &gov_attr_group, + NULL +}; + /** * devfreq_summary_show() - Show the summary of the devfreq devices * @s: seq_file instance to show the summary of devfreq devices |
