Re: [PATCH v2 15/18] ACPI: platform_profile: Remove platform_profile_handler from exported symbols

Rafael J. Wysocki
2025-01-14T17:14:01Z

On Tue, Jan 14, 2025 at 5:55 PM Ilpo Järvinen
<ilpo.jarvinen@linux.intel.com> wrote:
>
> On Tue, 14 Jan 2025, Kurt Borja wrote:
>
> > In order to protect the platform_profile_handler from API consumers,
> > allocate it in platform_profile_register() and modify it's signature
> > accordingly.
> >
> > Remove the platform_profile_handler from all consumer drivers and
> > replace them with a pointer to the class device, which is
> > now returned from platform_profile_register().
> >
> > Replace *pprof with a pointer to the class device in the rest of
> > exported symbols.
> >
> > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > ---
> > drivers/acpi/platform_profile.c | 87 ++++++++++++-------
> > .../surface/surface_platform_profile.c | 11 ++-
> > drivers/platform/x86/acer-wmi.c | 18 ++--
> > drivers/platform/x86/amd/pmf/pmf.h | 2 +-
> > drivers/platform/x86/amd/pmf/sps.c | 17 ++--
> > drivers/platform/x86/asus-wmi.c | 20 ++---
> > drivers/platform/x86/dell/alienware-wmi.c | 9 +-
> > drivers/platform/x86/dell/dell-pc.c | 22 ++---
> > drivers/platform/x86/hp/hp-wmi.c | 19 ++--
> > drivers/platform/x86/ideapad-laptop.c | 14 +--
> > .../platform/x86/inspur_platform_profile.c | 9 +-
> > drivers/platform/x86/thinkpad_acpi.c | 14 ++-
> > include/linux/platform_profile.h | 12 ++-
> > 13 files changed, 125 insertions(+), 129 deletions(-)
> >
> > diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
> > index 34e22b006ccc..2fae5e2fc962 100644
> > --- a/drivers/acpi/platform_profile.c
> > +++ b/drivers/acpi/platform_profile.c
> > @@ -4,6 +4,7 @@
> >
> > #include <linux/acpi.h>
> > #include <linux/bits.h>
> > +#include <linux/cleanup.h>
> > #include <linux/init.h>
> > #include <linux/kdev_t.h>
> > #include <linux/mutex.h>
> > @@ -213,9 +214,17 @@ static struct attribute *profile_attrs[] = {
> > };
> > ATTRIBUTE_GROUPS(profile);
> >
> > +static void pprof_device_release(struct device *dev)
> > +{
> > + struct platform_profile_handler *pprof = to_pprof_handler(dev);
> > +
> > + kfree(pprof);
> > +}
> > +
> > static const struct class platform_profile_class = {
> > .name = "platform-profile",
> > .dev_groups = profile_groups,
> > + .dev_release = pprof_device_release,
> > };
> >
> > /**
> > @@ -409,10 +418,10 @@ static const struct attribute_group platform_profile_group = {
> > .is_visible = profile_class_is_visible,
> > };
> >
> > -void platform_profile_notify(struct platform_profile_handler *pprof)
> > +void platform_profile_notify(struct device *dev)
> > {
> > scoped_cond_guard(mutex_intr, return, &profile_lock) {
> > - _notify_class_profile(&pprof->class_dev, NULL);
> > + _notify_class_profile(dev, NULL);
> > }
> > sysfs_notify(acpi_kobj, NULL, "platform_profile");
> > }
> > @@ -461,40 +470,51 @@ int platform_profile_cycle(void)
> > }
> > EXPORT_SYMBOL_GPL(platform_profile_cycle);
> >
> > -int platform_profile_register(struct platform_profile_handler *pprof, void *drvdata)
> > +struct device *platform_profile_register(struct device *dev, const char *name,
> > + void *drvdata,
> > + const struct platform_profile_ops *ops)
> > {
> > + int minor;
> > int err;
> >
> > - /* Sanity check the profile handler */
> > - if (!pprof || !pprof->ops->profile_set || !pprof->ops->profile_get ||
> > - !pprof->ops->probe) {
> > + /* Sanity check */
> > + if (!dev || !name || !ops || !ops->profile_get ||
> > + !ops->profile_set || !ops->probe) {
> > pr_err("platform_profile: handler is invalid\n");
> > - return -EINVAL;
> > + return ERR_PTR(-EINVAL);
> > }
> >
> > - err = pprof->ops->probe(drvdata, pprof->choices);
> > + struct platform_profile_handler *pprof __free(kfree) = kzalloc(
> > + sizeof(*pprof), GFP_KERNEL);
> > + if (!pprof)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + err = ops->probe(drvdata, pprof->choices);
> > if (err < 0)
> > - return err;
> > + return ERR_PTR(err);
> >
> > if (bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST)) {
> > pr_err("platform_profile: no available profiles\n");
> > - return -EINVAL;
> > + return ERR_PTR(-EINVAL);
> > }
> >
> > guard(mutex)(&profile_lock);
> >
> > /* create class interface for individual handler */
> > - pprof->minor = ida_alloc(&platform_profile_ida, GFP_KERNEL);
> > - if (pprof->minor < 0)
> > - return pprof->minor;
> > + minor = ida_alloc(&platform_profile_ida, GFP_KERNEL);
> > + if (minor < 0)
> > + return ERR_PTR(minor);
> >
> > + pprof->name = name;
> > + pprof->ops = ops;
> > + pprof->minor = minor;
> > pprof->class_dev.class = &platform_profile_class;
> > - pprof->class_dev.parent = pprof->dev;
> > + pprof->class_dev.parent = dev;
> > dev_set_drvdata(&pprof->class_dev, drvdata);
> > dev_set_name(&pprof->class_dev, "platform-profile-%d", pprof->minor);
> > err = device_register(&pprof->class_dev);
> > if (err) {
> > - put_device(&pprof->class_dev);
> > + put_device(&no_free_ptr(pprof)->class_dev);
> > goto cleanup_ida;
> > }
> >
> > @@ -504,20 +524,21 @@ int platform_profile_register(struct platform_profile_handler *pprof, void *drvd
> > if (err)
> > goto cleanup_cur;
> >
> > - return 0;
> > + return &no_free_ptr(pprof)->class_dev;
> >
> > cleanup_cur:
> > - device_unregister(&pprof->class_dev);
> > + device_unregister(&no_free_ptr(pprof)->class_dev);
>
> I don't like how this is architected.
>
> IMO, no_free_ptr() should not appear on error/rollback paths. The pointer
> is going to be freed despite the code just told it's not going to be
> freed, which sends conflicting signals. Obviously, it is because this
> function has relinquished its ownership of the pointer but as is it seems
> a dangerous/confusing pattern.

I agree.