[PATCH v2 12/15] ACPI: platform_profile: Make sure all profile handlers agree on profile

Mario Limonciello posted 15 patches 4 weeks ago
There is a newer version of this series
[PATCH v2 12/15] ACPI: platform_profile: Make sure all profile handlers agree on profile
Posted by Mario Limonciello 4 weeks ago
If for any reason multiple profile handlers don't agree on the profile
set for the system then the value shown in sysfs can be wrong.

Explicitly check that they match.

Tested-by: Matthew Schwartz <matthew.schwartz@linux.dev>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
---
 drivers/acpi/platform_profile.c | 61 ++++++++++++++++++++++++---------
 1 file changed, 45 insertions(+), 16 deletions(-)

diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
index db2ebd0393cf7..d22c4eb5f0c36 100644
--- a/drivers/acpi/platform_profile.c
+++ b/drivers/acpi/platform_profile.c
@@ -51,6 +51,45 @@ static unsigned long platform_profile_get_choices(void)
 	return seen;
 }
 
+/* expected to be called under mutex */
+static int platform_profile_get_active(enum platform_profile_option *profile)
+{
+	struct platform_profile_handler *handler;
+	enum platform_profile_option active = PLATFORM_PROFILE_LAST;
+	enum platform_profile_option active2 = PLATFORM_PROFILE_LAST;
+	int err;
+
+	list_for_each_entry(handler, &platform_profile_handler_list, list) {
+		if (active == PLATFORM_PROFILE_LAST)
+			err = handler->profile_get(handler, &active);
+		else
+			err = handler->profile_get(handler, &active2);
+		if (err) {
+			pr_err("Failed to get profile for handler %s\n", handler->name);
+			return err;
+		}
+
+		if (WARN_ON(active == PLATFORM_PROFILE_LAST))
+			return -EINVAL;
+		if (active2 == PLATFORM_PROFILE_LAST)
+			continue;
+
+		if (active != active2) {
+			pr_warn("Profile handlers don't agree on current profile\n");
+			return -EINVAL;
+		}
+		active = active2;
+	}
+
+	/* Check that profile is valid index */
+	if (WARN_ON((active < 0) || (active >= ARRAY_SIZE(profile_names))))
+		return -EIO;
+
+	*profile = active;
+
+	return 0;
+}
+
 static ssize_t platform_profile_choices_show(struct device *dev,
 					struct device_attribute *attr,
 					char *buf)
@@ -80,24 +119,14 @@ static ssize_t platform_profile_show(struct device *dev,
 	enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED;
 	int err;
 
-	err = mutex_lock_interruptible(&profile_lock);
-	if (err)
-		return err;
-
-	if (!cur_profile) {
-		mutex_unlock(&profile_lock);
-		return -ENODEV;
+	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+		if (!platform_profile_is_registered())
+			return -ENODEV;
+		err = platform_profile_get_active(&profile);
+		if (err)
+			return err;
 	}
 
-	err = cur_profile->profile_get(cur_profile, &profile);
-	mutex_unlock(&profile_lock);
-	if (err)
-		return err;
-
-	/* Check that profile is valid index */
-	if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names))))
-		return -EIO;
-
 	return sysfs_emit(buf, "%s\n", profile_names[profile]);
 }
 
-- 
2.43.0
Re: [PATCH v2 12/15] ACPI: platform_profile: Make sure all profile handlers agree on profile
Posted by Ilpo Järvinen 3 weeks, 6 days ago
On Sun, 27 Oct 2024, Mario Limonciello wrote:

> If for any reason multiple profile handlers don't agree on the profile
> set for the system then the value shown in sysfs can be wrong.
> 
> Explicitly check that they match.
> 
> Tested-by: Matthew Schwartz <matthew.schwartz@linux.dev>
> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
> ---
>  drivers/acpi/platform_profile.c | 61 ++++++++++++++++++++++++---------
>  1 file changed, 45 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
> index db2ebd0393cf7..d22c4eb5f0c36 100644
> --- a/drivers/acpi/platform_profile.c
> +++ b/drivers/acpi/platform_profile.c
> @@ -51,6 +51,45 @@ static unsigned long platform_profile_get_choices(void)
>  	return seen;
>  }
>  
> +/* expected to be called under mutex */

Don't add comments like this but enforce it with a lockdep annotation.

"mutex" would have been too vague anyway :-).

> +static int platform_profile_get_active(enum platform_profile_option *profile)
> +{
> +	struct platform_profile_handler *handler;
> +	enum platform_profile_option active = PLATFORM_PROFILE_LAST;
> +	enum platform_profile_option active2 = PLATFORM_PROFILE_LAST;
> +	int err;
> +
> +	list_for_each_entry(handler, &platform_profile_handler_list, list) {
> +		if (active == PLATFORM_PROFILE_LAST)
> +			err = handler->profile_get(handler, &active);
> +		else
> +			err = handler->profile_get(handler, &active2);
> +		if (err) {
> +			pr_err("Failed to get profile for handler %s\n", handler->name);
> +			return err;
> +		}
> +
> +		if (WARN_ON(active == PLATFORM_PROFILE_LAST))
> +			return -EINVAL;
> +		if (active2 == PLATFORM_PROFILE_LAST)
> +			continue;
> +
> +		if (active != active2) {
> +			pr_warn("Profile handlers don't agree on current profile\n");
> +			return -EINVAL;
> +		}
> +		active = active2;

This looked very confusing (IMO). How about this:

	enum platform_profile_option active = PLATFORM_PROFILE_LAST;
	enum platform_profile_option val;
	...

		err = handler->profile_get(handler, &val);
		if (err) {
			pr_err(...);
			return err;
		}

		if (WARN_ON(val == PLATFORM_PROFILE_LAST))
			return -EINVAL;

		if (active != val && active != PLATFORM_PROFILE_LAST) {
			pr_warn("Profile handlers don't agree on current profile\n");
			return -EINVAL;
		}
		active = val;

> +	}
> +
> +	/* Check that profile is valid index */
> +	if (WARN_ON((active < 0) || (active >= ARRAY_SIZE(profile_names))))

What does that < 0 check do? Should it be checked right after reading 
profile_get()? Or perhaps check both of these right there?

> +		return -EIO;
> +
> +	*profile = active;
> +
> +	return 0;
> +}
> +
>  static ssize_t platform_profile_choices_show(struct device *dev,
>  					struct device_attribute *attr,
>  					char *buf)
> @@ -80,24 +119,14 @@ static ssize_t platform_profile_show(struct device *dev,
>  	enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED;
>  	int err;
>  
> -	err = mutex_lock_interruptible(&profile_lock);
> -	if (err)
> -		return err;
> -
> -	if (!cur_profile) {
> -		mutex_unlock(&profile_lock);
> -		return -ENODEV;
> +	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {

scoped_cond_guard() conversion should be made in the guard patch?

> +		if (!platform_profile_is_registered())
> +			return -ENODEV;
> +		err = platform_profile_get_active(&profile);
> +		if (err)
> +			return err;
>  	}
>  
> -	err = cur_profile->profile_get(cur_profile, &profile);
> -	mutex_unlock(&profile_lock);
> -	if (err)
> -		return err;
> -
> -	/* Check that profile is valid index */
> -	if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names))))
> -		return -EIO;
> -
>  	return sysfs_emit(buf, "%s\n", profile_names[profile]);
>  }
>  
> 

-- 
 i.