On Tue, 19 Nov 2024, Mario Limonciello wrote: > When registering a platform profile handler create a class device > that will allow changing a single platform profile handler. > > The class and sysfs group are no longer needed when the platform profile > core is a module and unloaded, so remove them at that time as well. > > Reviewed-by: Armin Wolf <W_Armin@xxxxxx> > Tested-by: Mark Pearson <mpearson-lenovo@xxxxxxxxx> > Signed-off-by: Mario Limonciello <mario.limonciello@xxxxxxx> > --- > v7: > * Whitespace > * Add tag > * Drop class_is_registered() check > * Remove legacy sysfs before class > v6: > * Catch failures in ida_alloc > * Use 4th argument of device_create instead of dev_set_drvdata() > * Squash unregister patch > * Add module init callback > * Move class creation to module init > * Update visibility based on group presence > * Add back parent device > v5: > * Use ida instead of idr > * Use device_unregister instead of device_destroy() > * MKDEV (0, 0) > --- > drivers/acpi/platform_profile.c | 86 ++++++++++++++++++++++++++++++-- > include/linux/platform_profile.h | 2 + > 2 files changed, 83 insertions(+), 5 deletions(-) > > diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c > index 32affb75e782d..3524a2b4618ed 100644 > --- a/drivers/acpi/platform_profile.c > +++ b/drivers/acpi/platform_profile.c > @@ -5,6 +5,7 @@ > #include <linux/acpi.h> > #include <linux/bits.h> > #include <linux/init.h> > +#include <linux/kdev_t.h> > #include <linux/mutex.h> > #include <linux/platform_profile.h> > #include <linux/sysfs.h> > @@ -22,6 +23,12 @@ static const char * const profile_names[] = { > }; > static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST); > > +static DEFINE_IDA(platform_profile_ida); > + > +static const struct class platform_profile_class = { > + .name = "platform-profile", > +}; > + > static ssize_t platform_profile_choices_show(struct device *dev, > struct device_attribute *attr, > char *buf) > @@ -105,8 +112,25 @@ static struct attribute *platform_profile_attrs[] = { > NULL > }; > > +static int profile_class_registered(struct device *dev, const void *data) > +{ > + return 1; > +} > + > +static umode_t profile_class_is_visible(struct kobject *kobj, struct attribute *attr, int idx) > +{ > + if (!class_find_device(&platform_profile_class, NULL, NULL, profile_class_registered)) > + return 0; > + if (attr == &dev_attr_platform_profile_choices.attr) > + return 0444; > + if (attr == &dev_attr_platform_profile.attr) > + return 0644; These two should just return attr->mode I think. > + return 0; Is this even necessary for something? -- i. > +} > + > static const struct attribute_group platform_profile_group = { > - .attrs = platform_profile_attrs > + .attrs = platform_profile_attrs, > + .is_visible = profile_class_is_visible, > }; > > void platform_profile_notify(struct platform_profile_handler *pprof) > @@ -164,25 +188,77 @@ int platform_profile_register(struct platform_profile_handler *pprof) > if (cur_profile) > return -EEXIST; > > - err = sysfs_create_group(acpi_kobj, &platform_profile_group); > - if (err) > - return err; > + /* create class interface for individual handler */ > + pprof->minor = ida_alloc(&platform_profile_ida, GFP_KERNEL); > + if (pprof->minor < 0) > + return pprof->minor; > + pprof->class_dev = device_create(&platform_profile_class, pprof->dev, > + MKDEV(0, 0), pprof, "platform-profile-%d", > + pprof->minor); > + if (IS_ERR(pprof->class_dev)) { > + err = PTR_ERR(pprof->class_dev); > + goto cleanup_ida; > + } > > cur_profile = pprof; > + > + err = sysfs_update_group(acpi_kobj, &platform_profile_group); > + if (err) > + goto cleanup_cur; > + > return 0; > + > +cleanup_cur: > + cur_profile = NULL; > + device_unregister(pprof->class_dev); > + > +cleanup_ida: > + ida_free(&platform_profile_ida, pprof->minor); > + > + return err; > } > EXPORT_SYMBOL_GPL(platform_profile_register); > > int platform_profile_remove(struct platform_profile_handler *pprof) > { > + int id; > guard(mutex)(&profile_lock); > > - sysfs_remove_group(acpi_kobj, &platform_profile_group); > cur_profile = NULL; > + > + id = pprof->minor; > + device_unregister(pprof->class_dev); > + ida_free(&platform_profile_ida, id); > + > + sysfs_update_group(acpi_kobj, &platform_profile_group); > + > return 0; > } > EXPORT_SYMBOL_GPL(platform_profile_remove); > > +static int __init platform_profile_init(void) > +{ > + int err; > + > + err = class_register(&platform_profile_class); > + if (err) > + return err; > + > + err = sysfs_create_group(acpi_kobj, &platform_profile_group); > + if (err) > + class_unregister(&platform_profile_class); > + > + return err; > +} > + > +static void __exit platform_profile_exit(void) > +{ > + sysfs_remove_group(acpi_kobj, &platform_profile_group); > + class_unregister(&platform_profile_class); > +} > +module_init(platform_profile_init); > +module_exit(platform_profile_exit); > + > MODULE_AUTHOR("Mark Pearson <markpearson@xxxxxxxxxx>"); > MODULE_DESCRIPTION("ACPI platform profile sysfs interface"); > MODULE_LICENSE("GPL"); > diff --git a/include/linux/platform_profile.h b/include/linux/platform_profile.h > index 8ec0b8da56db5..a888fd085c513 100644 > --- a/include/linux/platform_profile.h > +++ b/include/linux/platform_profile.h > @@ -29,6 +29,8 @@ enum platform_profile_option { > struct platform_profile_handler { > const char *name; > struct device *dev; > + struct device *class_dev; > + int minor; > unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)]; > int (*profile_get)(struct platform_profile_handler *pprof, > enum platform_profile_option *profile); >