ACPI: platform_profile: Make sure all profile handlers agree on profile
authorMario Limonciello <mario.limonciello@amd.com>
Fri, 6 Dec 2024 03:19:13 +0000 (21:19 -0600)
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Tue, 10 Dec 2024 17:18:13 +0000 (19:18 +0200)
If for any reason multiple profile handlers don't agree on the profile
return the custom profile.

Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Link: https://lore.kernel.org/r/20241206031918.1537-18-mario.limonciello@amd.com
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
drivers/acpi/platform_profile.c

index 54483269b88344966c503ecafa97837204d5633d..95aff045a1eb3c19a72c9be5176c050fc7b6927d 100644 (file)
@@ -68,6 +68,24 @@ static int _store_class_profile(struct device *dev, void *data)
        return handler->profile_set(handler, *bit);
 }
 
+/**
+ * _notify_class_profile - Notify the class device of a profile change
+ * @dev: The class device
+ * @data: Unused
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _notify_class_profile(struct device *dev, void *data)
+{
+       struct platform_profile_handler *handler = dev_get_drvdata(dev);
+
+       lockdep_assert_held(&profile_lock);
+       sysfs_notify(&handler->class_dev->kobj, NULL, "profile");
+       kobject_uevent(&handler->class_dev->kobj, KOBJ_CHANGE);
+
+       return 0;
+}
+
 /**
  * get_class_profile - Show the current profile for a class device
  * @dev: The class device
@@ -251,51 +269,112 @@ static ssize_t platform_profile_choices_show(struct device *dev,
        return _commmon_choices_show(aggregate, buf);
 }
 
+/**
+ * _aggregate_profiles - Aggregate the profiles for legacy sysfs interface
+ * @dev: The device
+ * @data: The profile to return
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _aggregate_profiles(struct device *dev, void *data)
+{
+       enum platform_profile_option *profile = data;
+       enum platform_profile_option val;
+       int err;
+
+       err = get_class_profile(dev, &val);
+       if (err)
+               return err;
+
+       if (*profile != PLATFORM_PROFILE_LAST && *profile != val)
+               *profile = PLATFORM_PROFILE_CUSTOM;
+       else
+               *profile = val;
+
+       return 0;
+}
+
+/**
+ * _store_and_notify - Store and notify a class from legacy sysfs interface
+ * @dev: The device
+ * @data: The profile to return
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _store_and_notify(struct device *dev, void *data)
+{
+       enum platform_profile_option *profile = data;
+       int err;
+
+       err = _store_class_profile(dev, profile);
+       if (err)
+               return err;
+       return _notify_class_profile(dev, NULL);
+}
+
+/**
+ * platform_profile_show - Show the current profile for legacy sysfs interface
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
 static ssize_t platform_profile_show(struct device *dev,
-                                       struct device_attribute *attr,
-                                       char *buf)
+                                    struct device_attribute *attr,
+                                    char *buf)
 {
-       enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED;
+       enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
        int err;
 
        scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
-               if (!cur_profile)
-                       return -ENODEV;
-
-               err = cur_profile->profile_get(cur_profile, &profile);
+               err = class_for_each_device(&platform_profile_class, NULL,
+                                           &profile, _aggregate_profiles);
                if (err)
                        return err;
        }
 
-       /* Check that profile is valid index */
-       if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names))))
-               return -EIO;
+       /* no profile handler registered any more */
+       if (profile == PLATFORM_PROFILE_LAST)
+               return -EINVAL;
 
        return sysfs_emit(buf, "%s\n", profile_names[profile]);
 }
 
+/**
+ * platform_profile_store - Set the profile for legacy sysfs interface
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to read from
+ * @count: The number of bytes to read
+ *
+ * Return: The number of bytes read
+ */
 static ssize_t platform_profile_store(struct device *dev,
-                           struct device_attribute *attr,
-                           const char *buf, size_t count)
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
 {
-       int err, i;
+       unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
+       int ret;
+       int i;
 
        /* Scan for a matching profile */
        i = sysfs_match_string(profile_names, buf);
-       if (i < 0)
+       if (i < 0 || i == PLATFORM_PROFILE_CUSTOM)
                return -EINVAL;
-
+       set_bit(PLATFORM_PROFILE_LAST, choices);
        scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
-               if (!cur_profile)
-                       return -ENODEV;
-
-               /* Check that platform supports this profile choice */
-               if (!test_bit(i, cur_profile->choices))
+               ret = class_for_each_device(&platform_profile_class, NULL,
+                                           choices, _aggregate_choices);
+               if (ret)
+                       return ret;
+               if (!test_bit(i, choices))
                        return -EOPNOTSUPP;
 
-               err = cur_profile->profile_set(cur_profile, i);
-               if (err)
-                       return err;
+               ret = class_for_each_device(&platform_profile_class, NULL, &i,
+                                           _store_and_notify);
+               if (ret)
+                       return ret;
        }
 
        sysfs_notify(acpi_kobj, NULL, "platform_profile");