Linux Power Management development
 help / color / mirror / Atom feed
* [PATCH v2 0/5] Make platform profiles firmware independent
@ 2026-10-19  7:39 Daniel Lezcano
  2026-10-19  7:39 ` [PATCH v2 1/5] platform profile: Separate handler locking from sysfs lifetime Daniel Lezcano
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Daniel Lezcano @ 2026-10-19  7:39 UTC (permalink / raw)
  To: rafael
  Cc: Len Brown, Jonathan Corbet, Shuah Khan, Viresh Kumar,
	Hans de Goede, Ilpo Järvinen, Maximilian Luz,
	Shyam Sundar S K, Mark Pearson, Derek J. Clark, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Armin Wolf, Jai Luthra,
	Greg Kroah-Hartman, Mario Limonciello (AMD), Jack Wu,
	Mario Limonciello, Zhang DM, linux-acpi, linux-kernel, linux-doc,
	linux-pm, platform-driver-x86

Make platform profile selection independent of ACPI firmware.

The provider API already uses struct device, profile enums and callbacks;
it does not require an ACPI handle or firmware node. However, the core is
built under drivers/acpi/, its Kconfig symbol depends implicitly on ACPI,
and initialization fails when ACPI is disabled, preventing non-ACPI
providers from using the existing firmware-independent class interface.

This series moves the core to drivers/platform/profile/ and introduces
CONFIG_PLATFORM_PROFILE, updating all existing provider selectors. It
retains the tristate configuration and platform_profile module name.
Firmware-specific discovery and profile operations remain with providers.
The core can also be selected directly through its Kconfig prompt.

An optional ACPI adapter obtains the legacy sysfs kobject and is linked
into the same module. The generic core contains no ACPI types or symbols
and initializes without ACPI support or with ACPI disabled at boot.
The existing /sys/firmware/acpi/platform_profile and
/sys/firmware/acpi/platform_profile_choices attributes remain available
on active ACPI systems.

CONFIG_PLATFORM_PROFILE_LEGACY controls the backwards-compatible aggregate
sysfs interface. It defaults to enabled to preserve existing userspace.
Disabling it omits the legacy sysfs callbacks and ACPI adapter while
retaining class devices and kernel profile cycling. New userspace should
discover and control individual class devices instead.
The interface is deprecated, with removal intended after the 2028 LTS
kernel once userspace migration is complete. A shared pr_notice_once()
warns on legacy attribute access rather than on merely loading the core.
The default remains enabled while userspace adopts released class-device
support; this series does not claim that ecosystem migration is complete.

The existing firmware-independent interface remains:

  /sys/class/platform-profile/platform-profile-X/name
  /sys/class/platform-profile/platform-profile-X/choices
  /sys/class/platform-profile/platform-profile-X/profile

Non-ACPI userspace controls providers individually through these class
devices. Aggregate selection remains available through the legacy ACPI
attributes, and aggregate profile cycling remains available to kernel
callers. No new sysfs directory or userspace ABI is introduced.

The first patch fixes the lock/lifetime boundary: sysfs teardown must not
drain callbacks while holding the mutex those callbacks need. A separate
registration mutex serializes lifecycle
changes, while a profile-lock-protected handler list determines aggregate
membership independently of the class device's teardown state.

The second patch extracts the core and uses subsys_initcall_sync() so it
initializes after ACPI and before built-in thermal and CPUFreq consumers.
The third completes notifications on direct class-device writes and
restores aggregate visibility after registration failure. The fourth
documents the existing interfaces and extends maintainer coverage.
The fifth makes the legacy ACPI aggregate interface optional.

Validation:

  - ARM64 built-in core with CONFIG_ACPI=n: W=1 build and sparse.
  - x86 modular core with CONFIG_ACPI=y: W=1 object build and sparse.
    Both CONFIG_PLATFORM_PROFILE_LEGACY=y and =n are covered.
  - Full x86 kernel link and QEMU boot with lockdep and list debugging,
    with active ACPI and with acpi=off.
  - Firmware-less providers exercising common and hidden choices,
    profile reads/writes, invalid input, mixed profiles, cycling,
    partial callback failures and POLLPRI on the class and legacy ACPI
    interfaces. With acpi=off, class get/set, notifications and kernel
    cycling are exercised. Both runs check that no new aggregate sysfs
    directory is created.
  - Concurrent sysfs I/O with two persistent providers while a third
    provider is registered and removed 200 times, last-provider attribute
    removal and repeated provider module reloads.
  - QEMU with active ACPI and CONFIG_PLATFORM_PROFILE_LEGACY=n: verify
    the legacy attributes are absent, class get/set and notifications
    work, and kernel cycling and registration/removal stress pass.

The provider API and profile names are unchanged. Aggregate writes retain
their sequential, non-transactional behavior: a failing callback does not
roll back providers already updated.

Changes in v2:
  - Add the Fixes tag to patch 1 and explain how the mutex-guard conversion
    reintroduced the teardown deadlock previously fixed in 041142d7d252,
    and how class-device support subsequently extended the affected paths.
  - Drop the proposed /sys/kernel/platform_profile/ aggregate interface
    and its ABI documentation. Use the existing firmware-independent
    /sys/class/platform-profile/platform-profile-X/ devices instead.
  - Preserve aggregate selection through the legacy ACPI attributes and
    retain internal aggregation for kernel profile cycling.
  - Refocus patch 3 on class-device notifications for direct writes and
    restoring aggregate visibility after failed registration.
  - Update the Kconfig help and documentation to describe the existing
    class interface and distinguish it from ACPI aggregate selection.
  - Revalidate class controls and notifications with acpi=off, legacy
    aggregation with ACPI active, and the absence of a new aggregate ABI.
  - Add CONFIG_PLATFORM_PROFILE_LEGACY (default y) to make legacy aggregate
    sysfs code optional, independently of class devices and kernel cycling.
  - Mark the legacy ABI deprecated and describe removal after the 2028 LTS
    kernel, conditional on userspace migration. Emit one shared notice on
    legacy attribute access; retain the enabled default during migration.
  - Move the legacy ACPI ABI documentation from testing/ to obsolete/,
    document the replacement class attributes and update MAINTAINERS.

Changes in v1:
  - Initial series extracting the core from ACPI, separating handler
    locking from sysfs lifetime operations and retaining the legacy ABI.
  - Proposed a firmware-neutral aggregate sysfs interface alongside the
    existing per-provider class devices (removed in v2).

Daniel Lezcano (5):
  platform profile: Separate handler locking from sysfs lifetime
  platform profile: Extract the core from ACPI
  platform profile: Complete class notifications and registration cleanup
  platform profile: Document firmware-independent profile selection
  platform profile: Make legacy ACPI aggregate interface optional

 .../{testing => obsolete}/sysfs-platform_profile   |  12 ++
 .../ABI/testing/sysfs-class-platform-profile       |   2 +-
 .../userspace-api/sysfs-platform_profile.rst       |  55 ++++--
 MAINTAINERS                                        |   5 +
 drivers/acpi/Kconfig                               |   3 -
 drivers/acpi/Makefile                              |   1 -
 drivers/cpufreq/Kconfig.x86                        |   2 +-
 drivers/platform/Kconfig                           |  35 ++++
 drivers/platform/Makefile                          |   2 +
 drivers/platform/profile/Makefile                  |   5 +
 drivers/platform/profile/acpi.c                    |  15 ++
 .../platform_profile.c => platform/profile/core.c} | 195 +++++++++++++++------
 drivers/platform/profile/profile.h                 |  17 ++
 drivers/platform/surface/Kconfig                   |   6 +-
 drivers/platform/x86/Kconfig                       |  10 +-
 drivers/platform/x86/amd/pmf/Kconfig               |   2 +-
 drivers/platform/x86/dell/Kconfig                  |   4 +-
 drivers/platform/x86/hp/Kconfig                    |   2 +-
 drivers/platform/x86/lenovo/Kconfig                |   6 +-
 drivers/thermal/intel/int340x_thermal/Kconfig      |   2 +-
 include/linux/platform_profile.h                   |   3 +
 21 files changed, 296 insertions(+), 88 deletions(-)

base-commit: d0cd1e850e1602729dd60dc3c5fd96f80e0cdc5d
-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 1/5] platform profile: Separate handler locking from sysfs lifetime
  2026-10-19  7:39 [PATCH v2 0/5] Make platform profiles firmware independent Daniel Lezcano
@ 2026-10-19  7:39 ` Daniel Lezcano
  2026-10-19  8:50   ` Ilpo Järvinen
  2026-10-19  7:39 ` [PATCH v2 2/5] platform profile: Extract the core from ACPI Daniel Lezcano
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Daniel Lezcano @ 2026-10-19  7:39 UTC (permalink / raw)
  To: rafael
  Cc: Len Brown, Jonathan Corbet, Shuah Khan, Viresh Kumar,
	Hans de Goede, Ilpo Järvinen, Maximilian Luz,
	Shyam Sundar S K, Mark Pearson, Derek J. Clark, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Armin Wolf, Jai Luthra,
	Greg Kroah-Hartman, Mario Limonciello (AMD), Jack Wu,
	Mario Limonciello, Zhang DM, linux-acpi, linux-kernel, linux-doc,
	linux-pm, platform-driver-x86

Registration and removal currently hold profile_lock across
device_unregister() and sysfs_update_group(). These operations can remove
sysfs attributes and wait for their active callbacks to finish. The
profile read and write callbacks acquire profile_lock themselves, so a
concurrent access can deadlock:

  Removal/update path             Active sysfs callback
  -------------------             ---------------------
  acquire profile_lock            hold kernfs active reference
  remove a sysfs attribute         wait for profile_lock
  wait for its active references

The removal path cannot finish until the callback returns, and the
callback cannot return until the removal path releases profile_lock.
This applies both to per-provider device teardown and to updates of the
legacy aggregate attribute group.

Commit 041142d7d252 ("ACPI: platform-profile: Fix possible deadlock in
platform_profile_remove()") previously fixed this lock ordering by moving
sysfs_remove_group() outside profile_lock. Commit 55b1e68134bf ("ACPI:
platform_profile: Use guard(mutex) for register/unregister") reintroduced
the issue by extending the mutex guard over sysfs_remove_group(). Later,
commit 77be5cacb2c2 ("ACPI: platform_profile: Create class for ACPI
platform profile") replaced that teardown with device_unregister() and
sysfs_update_group(), still under the same mutex, extending the problem
to class-device teardown and aggregate attribute updates.

Simply dropping profile_lock around these operations is insufficient.
Aggregate operations currently discover providers through the class
device list. A provider being unregistered could therefore still be
selected for an aggregate read, write or cycle while its sysfs files are
being torn down. Concurrent registrations and removals would also race
when updating aggregate attribute visibility.

Introduce profile_registration_lock to serialize registration, removal
and aggregate sysfs updates. Sysfs callbacks never acquire this mutex.
Retain profile_lock for profile operations and use it to protect an
explicit list of handlers participating in the aggregate interface.

Add a handler to that list only after device_register() succeeds. On
removal, unlink it under profile_lock before calling device_unregister().
Acquiring profile_lock first waits for any aggregate operation already
using the handler to complete; unlinking prevents later aggregate
operations from finding it. Release profile_lock before draining sysfs
callbacks, allowing active per-provider callbacks to complete before the
device is released. Apply the same unlink-before-unregister ordering to
registration error cleanup.

Walk this list for aggregate reads, writes and profile cycling, and use
its emptiness to determine aggregate attribute visibility. The class
devices remain the per-provider userspace interface.

There can now be an interval between removing the last handler from the
list and removing the aggregate attributes. Explicitly reject choices
reads and writes during that interval: the intersection bitmap starts
with every bit set, so an empty traversal must not advertise all profiles
or report a successful write without updating a provider.

This separates aggregate membership from class device teardown while
preserving the existing class and ACPI sysfs interfaces.

Fixes: 55b1e68134bf ("ACPI: platform_profile: Use guard(mutex) for register/unregister")
Assisted-by: OpenCode:gpt-6.1-sol
Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
---
 drivers/acpi/platform_profile.c | 75 ++++++++++++++++++---------------
 1 file changed, 42 insertions(+), 33 deletions(-)

diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
index 79d2e92e5c28..f98ce4cda02c 100644
--- a/drivers/acpi/platform_profile.c
+++ b/drivers/acpi/platform_profile.c
@@ -8,6 +8,7 @@
 #include <linux/bits.h>
 #include <linux/cleanup.h>
 #include <linux/init.h>
+#include <linux/list.h>
 #include <linux/mutex.h>
 #include <linux/platform_profile.h>
 #include <linux/sysfs.h>
@@ -15,8 +16,12 @@
 #define to_pprof_handler(d)	(container_of(d, struct platform_profile_handler, dev))
 
 static DEFINE_MUTEX(profile_lock);
+/* Serialize registration and sysfs updates without blocking sysfs callbacks. */
+static DEFINE_MUTEX(profile_registration_lock);
+static LIST_HEAD(profile_handlers);
 
 struct platform_profile_handler {
+	struct list_head node;
 	const char *name;
 	struct device dev;
 	int minor;
@@ -44,6 +49,22 @@ static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST);
 
 static DEFINE_IDA(platform_profile_ida);
 
+static int profile_walk_handlers(void *data,
+				 int (*fn)(struct device *dev, void *data))
+{
+	struct platform_profile_handler *handler;
+	int ret;
+
+	lockdep_assert_held(&profile_lock);
+	list_for_each_entry(handler, &profile_handlers, node) {
+		ret = fn(&handler->dev, data);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 /**
  * _commmon_choices_show - Show the available profile choices
  * @choices: The available profile choices
@@ -306,20 +327,18 @@ static ssize_t platform_profile_choices_show(struct kobject *kobj,
 	int err;
 
 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
-		err = class_for_each_device(&platform_profile_class, NULL,
-					    &data, _aggregate_choices);
+		err = profile_walk_handlers(&data, _aggregate_choices);
 		if (err)
 			return err;
 		if (data.count == 1) {
-			err = class_for_each_device(&platform_profile_class, NULL,
-						    &data, _remove_hidden_choices);
+			err = profile_walk_handlers(&data, _remove_hidden_choices);
 			if (err)
 				return err;
 		}
 	}
 
 	/* no profile handler registered any more */
-	if (bitmap_empty(data.aggregate, PLATFORM_PROFILE_LAST))
+	if (!data.count || bitmap_empty(data.aggregate, PLATFORM_PROFILE_LAST))
 		return -EINVAL;
 
 	return _commmon_choices_show(data.aggregate, buf);
@@ -384,8 +403,7 @@ static ssize_t platform_profile_show(struct kobject *kobj,
 	int err;
 
 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
-		err = class_for_each_device(&platform_profile_class, NULL,
-					    &profile, _aggregate_profiles);
+		err = profile_walk_handlers(&profile, _aggregate_profiles);
 		if (err)
 			return err;
 	}
@@ -423,15 +441,13 @@ static ssize_t platform_profile_store(struct kobject *kobj,
 		return -EINVAL;
 
 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
-		ret = class_for_each_device(&platform_profile_class, NULL,
-					    &data, _aggregate_choices);
+		ret = profile_walk_handlers(&data, _aggregate_choices);
 		if (ret)
 			return ret;
-		if (!test_bit(i, data.aggregate))
+		if (!data.count || !test_bit(i, data.aggregate))
 			return -EOPNOTSUPP;
 
-		ret = class_for_each_device(&platform_profile_class, NULL, &i,
-					    _store_and_notify);
+		ret = profile_walk_handlers(&i, _store_and_notify);
 		if (ret)
 			return ret;
 	}
@@ -450,22 +466,11 @@ 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)
 {
-	struct device *dev;
-
-	dev = class_find_device(&platform_profile_class, NULL, NULL, profile_class_registered);
-	if (!dev)
-		return 0;
-
-	put_device(dev);
+	guard(mutex)(&profile_lock);
 
-	return attr->mode;
+	return list_empty(&profile_handlers) ? 0 : attr->mode;
 }
 
 static const struct attribute_group platform_profile_group = {
@@ -502,8 +507,7 @@ int platform_profile_cycle(void)
 	int err;
 
 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
-		err = class_for_each_device(&platform_profile_class, NULL,
-					    &profile, _aggregate_profiles);
+		err = profile_walk_handlers(&profile, _aggregate_profiles);
 		if (err)
 			return err;
 
@@ -512,8 +516,7 @@ int platform_profile_cycle(void)
 		    profile == PLATFORM_PROFILE_LAST)
 			return -EINVAL;
 
-		err = class_for_each_device(&platform_profile_class, NULL,
-					    &data, _aggregate_choices);
+		err = profile_walk_handlers(&data, _aggregate_choices);
 		if (err)
 			return err;
 
@@ -525,8 +528,7 @@ int platform_profile_cycle(void)
 					  PLATFORM_PROFILE_LAST,
 					  profile + 1);
 
-		err = class_for_each_device(&platform_profile_class, NULL, &next,
-					    _store_and_notify);
+		err = profile_walk_handlers(&next, _store_and_notify);
 
 		if (err)
 			return err;
@@ -583,7 +585,7 @@ struct device *platform_profile_register(struct device *dev, const char *name,
 		}
 	}
 
-	guard(mutex)(&profile_lock);
+	guard(mutex)(&profile_registration_lock);
 
 	/* create class interface for individual handler */
 	minor = ida_alloc(&platform_profile_ida, GFP_KERNEL);
@@ -604,6 +606,8 @@ struct device *platform_profile_register(struct device *dev, const char *name,
 		put_device(ppdev);
 		goto cleanup_ida;
 	}
+	scoped_guard(mutex, &profile_lock)
+		list_add_tail(&to_pprof_handler(ppdev)->node, &profile_handlers);
 
 	sysfs_notify(acpi_kobj, NULL, "platform_profile");
 
@@ -614,6 +618,8 @@ struct device *platform_profile_register(struct device *dev, const char *name,
 	return ppdev;
 
 cleanup_cur:
+	scoped_guard(mutex, &profile_lock)
+		list_del(&to_pprof_handler(ppdev)->node);
 	device_unregister(ppdev);
 
 cleanup_ida:
@@ -636,7 +642,10 @@ void platform_profile_remove(struct device *dev)
 
 	pprof = to_pprof_handler(dev);
 
-	guard(mutex)(&profile_lock);
+	guard(mutex)(&profile_registration_lock);
+
+	scoped_guard(mutex, &profile_lock)
+		list_del(&pprof->node);
 
 	ida_free(&platform_profile_ida, pprof->minor);
 	device_unregister(&pprof->dev);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 2/5] platform profile: Extract the core from ACPI
  2026-10-19  7:39 [PATCH v2 0/5] Make platform profiles firmware independent Daniel Lezcano
  2026-10-19  7:39 ` [PATCH v2 1/5] platform profile: Separate handler locking from sysfs lifetime Daniel Lezcano
@ 2026-10-19  7:39 ` Daniel Lezcano
  2026-10-19  7:39 ` [PATCH v2 3/5] platform profile: Complete class notifications and registration cleanup Daniel Lezcano
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Daniel Lezcano @ 2026-10-19  7:39 UTC (permalink / raw)
  To: rafael
  Cc: Len Brown, Jonathan Corbet, Shuah Khan, Viresh Kumar,
	Hans de Goede, Ilpo Järvinen, Maximilian Luz,
	Shyam Sundar S K, Mark Pearson, Derek J. Clark, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Armin Wolf, Jai Luthra,
	Greg Kroah-Hartman, Mario Limonciello (AMD), Jack Wu,
	Mario Limonciello, Zhang DM, linux-acpi, linux-kernel, linux-doc,
	linux-pm, platform-driver-x86

Platform profile providers already use device-based callbacks and do not
need ACPI types. The core is nevertheless confined to the ACPI build and
refuses to initialize when ACPI is disabled.

Move the implementation to drivers/platform/profile and introduce the
firmware-independent PLATFORM_PROFILE symbol. Update all provider
selectors. Keep provider-specific ACPI dependencies and the
platform_profile module name.

Isolate access to the legacy ACPI kobject in an optional compatibility
object linked into the same module. Retain the legacy ABI on active ACPI
systems, and initialize the class even when ACPI is absent or disabled.

Use subsys_initcall_sync so the class is ready before built-in thermal
and CPUFreq providers, but after ACPI has initialized its compatibility
kobject.

Assisted-by: OpenCode:gpt-6.1-sol
Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
---
 drivers/acpi/Kconfig                          |  3 -
 drivers/acpi/Makefile                         |  1 -
 drivers/cpufreq/Kconfig.x86                   |  2 +-
 drivers/platform/Kconfig                      | 16 +++++
 drivers/platform/Makefile                     |  2 +
 drivers/platform/profile/Makefile             |  5 ++
 drivers/platform/profile/acpi.c               | 15 +++++
 .../profile/core.c}                           | 64 +++++++++++++------
 drivers/platform/profile/profile.h            | 17 +++++
 drivers/platform/surface/Kconfig              |  6 +-
 drivers/platform/x86/Kconfig                  | 10 +--
 drivers/platform/x86/amd/pmf/Kconfig          |  2 +-
 drivers/platform/x86/dell/Kconfig             |  4 +-
 drivers/platform/x86/hp/Kconfig               |  2 +-
 drivers/platform/x86/lenovo/Kconfig           |  6 +-
 drivers/thermal/intel/int340x_thermal/Kconfig |  2 +-
 16 files changed, 118 insertions(+), 39 deletions(-)
 create mode 100644 drivers/platform/profile/Makefile
 create mode 100644 drivers/platform/profile/acpi.c
 rename drivers/{acpi/platform_profile.c => platform/profile/core.c} (94%)
 create mode 100644 drivers/platform/profile/profile.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index f165d14cf61a..dd00312217de 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -351,9 +351,6 @@ config ACPI_THERMAL
 	  To compile this driver as a module, choose M here:
 	  the module will be called thermal.
 
-config ACPI_PLATFORM_PROFILE
-	tristate
-
 config ACPI_CUSTOM_DSDT_FILE
 	string "Custom DSDT Table file to include"
 	default ""
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index d1b0affb844f..f8af27341805 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -91,7 +91,6 @@ obj-$(CONFIG_ACPI_PROCESSOR)	+= processor.o
 obj-$(CONFIG_ACPI)		+= container.o
 obj-$(CONFIG_ACPI_THERMAL_LIB)	+= thermal_lib.o
 obj-$(CONFIG_ACPI_THERMAL)	+= thermal.o
-obj-$(CONFIG_ACPI_PLATFORM_PROFILE) 	+= platform_profile.o
 obj-$(CONFIG_ACPI_NFIT)		+= nfit/
 obj-$(CONFIG_ACPI_NHLT)		+= nhlt.o
 obj-$(CONFIG_ACPI_NUMA)		+= numa/
diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86
index d6190b8dde4b..9e1e3391a084 100644
--- a/drivers/cpufreq/Kconfig.x86
+++ b/drivers/cpufreq/Kconfig.x86
@@ -39,7 +39,7 @@ config X86_AMD_PSTATE
 	select ACPI_PROCESSOR
 	select ACPI_CPPC_LIB if X86_64
 	select CPU_FREQ_GOV_SCHEDUTIL if SMP
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	select POWER_SUPPLY
 	help
 	  This driver adds a CPUFreq driver which utilizes a fine grain
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 312788f249c9..395b75f2edf0 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -1,4 +1,20 @@
 # SPDX-License-Identifier: GPL-2.0-only
+config PLATFORM_PROFILE
+	tristate "Platform profile support"
+	help
+	  Provide a common sysfs interface for selecting platform power,
+	  performance and cooling profiles. Platform-specific provider drivers
+	  map their operating modes onto standard profile names, independently
+	  of the firmware interface they use.
+
+	  The core exposes per-provider controls under /sys/class/platform-profile/.
+	  A provider driver must register before profile attributes become
+	  available. ACPI is not required; on ACPI systems the legacy system-wide
+	  interface under /sys/firmware/acpi/ is also provided.
+
+	  To compile this as a module, choose M here: the module will be called
+	  platform_profile.
+
 source "drivers/platform/mips/Kconfig"
 
 source "drivers/platform/loongarch/Kconfig"
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index fa322e7f8716..b6732f2f3c85 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -3,6 +3,8 @@
 # Makefile for linux/drivers/platform
 #
 
+obj-$(CONFIG_PLATFORM_PROFILE)	+= profile/
+
 obj-$(CONFIG_X86)		+= x86/
 obj-$(CONFIG_LOONGARCH)		+= loongarch/
 obj-$(CONFIG_MELLANOX_PLATFORM)	+= mellanox/
diff --git a/drivers/platform/profile/Makefile b/drivers/platform/profile/Makefile
new file mode 100644
index 000000000000..ae95ba09383c
--- /dev/null
+++ b/drivers/platform/profile/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PLATFORM_PROFILE) += platform_profile.o
+
+platform_profile-y := core.o
+platform_profile-$(CONFIG_ACPI) += acpi.o
diff --git a/drivers/platform/profile/acpi.c b/drivers/platform/profile/acpi.c
new file mode 100644
index 000000000000..ff925dedacaf
--- /dev/null
+++ b/drivers/platform/profile/acpi.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Compatibility with the original ACPI platform profile sysfs ABI. */
+
+#include <linux/acpi.h>
+#include <linux/kobject.h>
+
+#include "profile.h"
+
+struct kobject *platform_profile_compat_kobj(void)
+{
+	if (acpi_disabled)
+		return NULL;
+
+	return kobject_get(acpi_kobj);
+}
diff --git a/drivers/acpi/platform_profile.c b/drivers/platform/profile/core.c
similarity index 94%
rename from drivers/acpi/platform_profile.c
rename to drivers/platform/profile/core.c
index f98ce4cda02c..f18fd96b942c 100644
--- a/drivers/acpi/platform_profile.c
+++ b/drivers/platform/profile/core.c
@@ -4,21 +4,36 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include <linux/acpi.h>
+#include <linux/bitmap.h>
 #include <linux/bits.h>
 #include <linux/cleanup.h>
 #include <linux/init.h>
+#include <linux/idr.h>
+#include <linux/kobject.h>
 #include <linux/list.h>
+#include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/platform_profile.h>
+#include <linux/slab.h>
 #include <linux/sysfs.h>
 
+#include "profile.h"
+
 #define to_pprof_handler(d)	(container_of(d, struct platform_profile_handler, dev))
 
 static DEFINE_MUTEX(profile_lock);
 /* Serialize registration and sysfs updates without blocking sysfs callbacks. */
 static DEFINE_MUTEX(profile_registration_lock);
 static LIST_HEAD(profile_handlers);
+static struct kobject *compat_kobj;
+
+static void profile_notify(void)
+{
+	if (compat_kobj)
+		sysfs_notify(compat_kobj, NULL, "platform_profile");
+}
+
+static int profile_update(void);
 
 struct platform_profile_handler {
 	struct list_head node;
@@ -237,7 +252,7 @@ static ssize_t profile_store(struct device *dev,
 			return ret;
 	}
 
-	sysfs_notify(acpi_kobj, NULL, "platform_profile");
+	profile_notify();
 
 	return count;
 }
@@ -452,7 +467,7 @@ static ssize_t platform_profile_store(struct kobject *kobj,
 			return ret;
 	}
 
-	sysfs_notify(acpi_kobj, NULL, "platform_profile");
+	profile_notify();
 
 	return count;
 }
@@ -478,6 +493,14 @@ static const struct attribute_group platform_profile_group = {
 	.is_visible = profile_class_is_visible,
 };
 
+static int profile_update(void)
+{
+	if (compat_kobj)
+		return sysfs_update_group(compat_kobj, &platform_profile_group);
+
+	return 0;
+}
+
 /**
  * platform_profile_notify - Notify class device and legacy sysfs interface
  * @dev: The class device
@@ -487,7 +510,7 @@ void platform_profile_notify(struct device *dev)
 	scoped_cond_guard(mutex_intr, return, &profile_lock) {
 		_notify_class_profile(dev, NULL);
 	}
-	sysfs_notify(acpi_kobj, NULL, "platform_profile");
+	profile_notify();
 }
 EXPORT_SYMBOL_GPL(platform_profile_notify);
 
@@ -534,7 +557,7 @@ int platform_profile_cycle(void)
 			return err;
 	}
 
-	sysfs_notify(acpi_kobj, NULL, "platform_profile");
+	profile_notify();
 
 	return 0;
 }
@@ -609,9 +632,9 @@ struct device *platform_profile_register(struct device *dev, const char *name,
 	scoped_guard(mutex, &profile_lock)
 		list_add_tail(&to_pprof_handler(ppdev)->node, &profile_handlers);
 
-	sysfs_notify(acpi_kobj, NULL, "platform_profile");
+	profile_notify();
 
-	err = sysfs_update_group(acpi_kobj, &platform_profile_group);
+	err = profile_update();
 	if (err)
 		goto cleanup_cur;
 
@@ -650,8 +673,8 @@ void platform_profile_remove(struct device *dev)
 	ida_free(&platform_profile_ida, pprof->minor);
 	device_unregister(&pprof->dev);
 
-	sysfs_notify(acpi_kobj, NULL, "platform_profile");
-	sysfs_update_group(acpi_kobj, &platform_profile_group);
+	profile_notify();
+	profile_update();
 }
 EXPORT_SYMBOL_GPL(platform_profile_remove);
 
@@ -699,28 +722,33 @@ static int __init platform_profile_init(void)
 {
 	int err;
 
-	if (acpi_disabled)
-		return -EOPNOTSUPP;
-
 	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);
+	compat_kobj = platform_profile_compat_kobj();
+	if (compat_kobj) {
+		err = sysfs_create_group(compat_kobj, &platform_profile_group);
+		if (err) {
+			kobject_put(compat_kobj);
+			class_unregister(&platform_profile_class);
+		}
+	}
 
 	return err;
 }
 
 static void __exit platform_profile_exit(void)
 {
-	sysfs_remove_group(acpi_kobj, &platform_profile_group);
+	if (compat_kobj) {
+		sysfs_remove_group(compat_kobj, &platform_profile_group);
+		kobject_put(compat_kobj);
+	}
 	class_unregister(&platform_profile_class);
 }
-module_init(platform_profile_init);
+subsys_initcall_sync(platform_profile_init);
 module_exit(platform_profile_exit);
 
 MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
-MODULE_DESCRIPTION("ACPI platform profile sysfs interface");
+MODULE_DESCRIPTION("Platform profile sysfs interface");
 MODULE_LICENSE("GPL");
diff --git a/drivers/platform/profile/profile.h b/drivers/platform/profile/profile.h
new file mode 100644
index 000000000000..910c9f7da6a0
--- /dev/null
+++ b/drivers/platform/profile/profile.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _PLATFORM_PROFILE_INTERNAL_H
+#define _PLATFORM_PROFILE_INTERNAL_H
+
+#include <linux/kconfig.h>
+#include <linux/kobject.h>
+
+#ifdef CONFIG_ACPI
+struct kobject *platform_profile_compat_kobj(void);
+#else
+static inline struct kobject *platform_profile_compat_kobj(void)
+{
+	return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig
index f775c6ca1ec1..08f15f72f8ed 100644
--- a/drivers/platform/surface/Kconfig
+++ b/drivers/platform/surface/Kconfig
@@ -203,12 +203,12 @@ config SURFACE_PLATFORM_PROFILE
 	tristate "Surface Platform Profile Driver"
 	depends on ACPI
 	depends on SURFACE_AGGREGATOR_REGISTRY
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	help
-	  Provides support for the ACPI platform profile on 5th- and later
+	  Provides support for platform profiles on 5th- and later
 	  generation Microsoft Surface devices.
 
-	  More specifically, this driver provides ACPI platform profile support
+	  More specifically, this driver provides platform profile support
 	  on Microsoft Surface devices with a Surface System Aggregator Module
 	  (SSAM) connected via the Surface Serial Hub (SSH / SAM-over-SSH). In
 	  other words, this driver provides platform profile support on the
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index b54b5212b204..76b42f68c9cc 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -120,7 +120,7 @@ config BITLAND_MIFS_WMI
 	depends on INPUT
 	depends on LEDS_CLASS
 	depends on POWER_SUPPLY
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	select INPUT_SPARSEKMAP
 	help
 	  This is a driver for Bitland MiInterface based laptops.
@@ -180,7 +180,7 @@ config ACER_WMI
 	select INPUT_SPARSEKMAP
 	select LEDS_CLASS
 	select NEW_LEDS
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	help
 	  This is a driver for newer Acer (and Wistron) laptops. It adds
 	  wireless radio and bluetooth control, and on some laptops,
@@ -279,7 +279,7 @@ config ASUS_WMI
 	select INPUT_SPARSEKMAP
 	select LEDS_CLASS
 	select NEW_LEDS
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	help
 	  Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new
 	  Asus Notebooks).
@@ -646,7 +646,7 @@ config SAMSUNG_GALAXYBOOK
 	depends on INPUT
 	depends on LEDS_CLASS
 	depends on SERIO_I8042
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	select FW_ATTR_CLASS
 	help
 	  This is a driver for Samsung Galaxy Book series notebooks. It adds
@@ -906,7 +906,7 @@ config TOUCHSCREEN_DMI
 config INSPUR_PLATFORM_PROFILE
 	tristate "Inspur WMI platform profile driver"
 	depends on ACPI_WMI
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	help
 	This will allow users to determine and control the platform modes
 	between low-power, balanced and performance modes.
diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig
index 25b8f7ae3abd..a0bdd85db6a0 100644
--- a/drivers/platform/x86/amd/pmf/Kconfig
+++ b/drivers/platform/x86/amd/pmf/Kconfig
@@ -8,7 +8,7 @@ config AMD_PMF
 	depends on ACPI && PCI
 	depends on POWER_SUPPLY
 	depends on AMD_NODE
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	depends on TEE && AMDTEE
 	depends on AMD_SFH_HID
 	depends on HAS_IOMEM
diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 9f0016056d7e..f6acf1cc2a2f 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -44,7 +44,7 @@ config ALIENWARE_WMI_WMAX
 	bool "Alienware WMAX WMI device driver"
 	default y
 	depends on ALIENWARE_WMI
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	help
 	 Alienware WMI driver with AlienFX LED, HDMI, amplifier, deep sleep and
 	 AWCC thermal control capabilities.
@@ -120,7 +120,7 @@ config DELL_PC
 	depends on ACPI
 	depends on DMI
 	depends on DELL_SMBIOS
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	help
 	This driver adds support for controlling the fan modes via platform_profile
 	on supported Dell systems regardless of formfactor.
diff --git a/drivers/platform/x86/hp/Kconfig b/drivers/platform/x86/hp/Kconfig
index dd51491b9bcd..81a52194ff42 100644
--- a/drivers/platform/x86/hp/Kconfig
+++ b/drivers/platform/x86/hp/Kconfig
@@ -43,7 +43,7 @@ config HP_WMI
 	depends on RFKILL || RFKILL = n
 	select POWER_SUPPLY
 	select INPUT_SPARSEKMAP
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	select HWMON
 	help
 	  Say Y here if you want to support WMI-based hotkeys on HP laptops and
diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig
index 4443f40ef8aa..c8606ad56139 100644
--- a/drivers/platform/x86/lenovo/Kconfig
+++ b/drivers/platform/x86/lenovo/Kconfig
@@ -12,7 +12,7 @@ config IDEAPAD_LAPTOP
 	depends on BACKLIGHT_CLASS_DEVICE
 	depends on ACPI_VIDEO || ACPI_VIDEO = n
 	depends on ACPI_WMI || ACPI_WMI = n
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	select INPUT_SPARSEKMAP
 	select NEW_LEDS
 	select LEDS_CLASS
@@ -63,7 +63,7 @@ config THINKPAD_ACPI
 	depends on BACKLIGHT_CLASS_DEVICE
 	depends on I2C
 	depends on DRM
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	select DRM_PRIVACY_SCREEN
 	select HWMON
 	select NVRAM
@@ -250,7 +250,7 @@ config LENOVO_WMI_GAMEZONE
 	tristate "Lenovo GameZone WMI Driver"
 	depends on ACPI_WMI
 	depends on DMI
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	select LENOVO_WMI_EVENTS
 	select LENOVO_WMI_HELPERS
 	help
diff --git a/drivers/thermal/intel/int340x_thermal/Kconfig b/drivers/thermal/intel/int340x_thermal/Kconfig
index 4ced7bdcd62c..3026a0a8d073 100644
--- a/drivers/thermal/intel/int340x_thermal/Kconfig
+++ b/drivers/thermal/intel/int340x_thermal/Kconfig
@@ -12,7 +12,7 @@ config INT340X_THERMAL
 	select ACPI_THERMAL_LIB
 	select INTEL_SOC_DTS_IOSF_CORE
 	select INTEL_TCC
-	select ACPI_PLATFORM_PROFILE
+	select PLATFORM_PROFILE
 	select PROC_THERMAL_MMIO_RAPL if POWERCAP
 	help
 	  Newer laptops and tablets that use ACPI may have thermal sensors and
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 3/5] platform profile: Complete class notifications and registration cleanup
  2026-10-19  7:39 [PATCH v2 0/5] Make platform profiles firmware independent Daniel Lezcano
  2026-10-19  7:39 ` [PATCH v2 1/5] platform profile: Separate handler locking from sysfs lifetime Daniel Lezcano
  2026-10-19  7:39 ` [PATCH v2 2/5] platform profile: Extract the core from ACPI Daniel Lezcano
@ 2026-10-19  7:39 ` Daniel Lezcano
  2026-10-19  7:39 ` [PATCH v2 4/5] platform profile: Document firmware-independent profile selection Daniel Lezcano
  2026-10-19  7:39 ` [PATCH v2 5/5] platform profile: Make legacy ACPI aggregate interface optional Daniel Lezcano
  4 siblings, 0 replies; 9+ messages in thread
From: Daniel Lezcano @ 2026-10-19  7:39 UTC (permalink / raw)
  To: rafael
  Cc: Len Brown, Jonathan Corbet, Shuah Khan, Viresh Kumar,
	Hans de Goede, Ilpo Järvinen, Maximilian Luz,
	Shyam Sundar S K, Mark Pearson, Derek J. Clark, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Armin Wolf, Jai Luthra,
	Greg Kroah-Hartman, Mario Limonciello (AMD), Jack Wu,
	Mario Limonciello, Zhang DM, linux-acpi, linux-kernel, linux-doc,
	linux-pm, platform-driver-x86

The class profile attribute documents POLLPRI notifications for changes
made through userspace writes as well as changes reported by providers.
However, a direct write currently notifies only the legacy ACPI aggregate
attribute, leaving listeners on the provider's own profile attribute
unaware of the change.

Notify the class device after a successful direct write, using the same
helper as aggregate writes and provider-originated notifications. This
also makes notifications available when ACPI is absent or disabled.

Restore aggregate attribute visibility after unlinking a handler whose
registration failed during a sysfs group update. Keep aggregation exposed
only through the existing ACPI interface; the existing class devices
provide firmware-independent userspace access.

Assisted-by: OpenCode:gpt-6.1-sol
Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
---
 drivers/platform/profile/core.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/platform/profile/core.c b/drivers/platform/profile/core.c
index f18fd96b942c..320a5a964fea 100644
--- a/drivers/platform/profile/core.c
+++ b/drivers/platform/profile/core.c
@@ -250,6 +250,7 @@ static ssize_t profile_store(struct device *dev,
 		ret = _store_class_profile(dev, &index);
 		if (ret)
 			return ret;
+		_notify_class_profile(dev, NULL);
 	}
 
 	profile_notify();
@@ -324,7 +325,7 @@ static int _remove_hidden_choices(struct device *dev, void *arg)
 }
 
 /**
- * platform_profile_choices_show - Show the available profile choices for legacy sysfs interface
+ * platform_profile_choices_show - Show the aggregate profile choices
  * @kobj: The kobject
  * @attr: The attribute
  * @buf: The buffer to write to
@@ -360,7 +361,7 @@ static ssize_t platform_profile_choices_show(struct kobject *kobj,
 }
 
 /**
- * _aggregate_profiles - Aggregate the profiles for legacy sysfs interface
+ * _aggregate_profiles - Aggregate the profiles across providers
  * @dev: The device
  * @data: The profile to return
  *
@@ -385,7 +386,7 @@ static int _aggregate_profiles(struct device *dev, void *data)
 }
 
 /**
- * _store_and_notify - Store and notify a class from legacy sysfs interface
+ * _store_and_notify - Store and notify a provider from an aggregate write
  * @dev: The device
  * @data: The profile to return
  *
@@ -403,7 +404,7 @@ static int _store_and_notify(struct device *dev, void *data)
 }
 
 /**
- * platform_profile_show - Show the current profile for legacy sysfs interface
+ * platform_profile_show - Show the current aggregate profile
  * @kobj: The kobject
  * @attr: The attribute
  * @buf: The buffer to write to
@@ -431,7 +432,7 @@ static ssize_t platform_profile_show(struct kobject *kobj,
 }
 
 /**
- * platform_profile_store - Set the profile for legacy sysfs interface
+ * platform_profile_store - Set the profile across all providers
  * @kobj: The kobject
  * @attr: The attribute
  * @buf: The buffer to read from
@@ -502,7 +503,7 @@ static int profile_update(void)
 }
 
 /**
- * platform_profile_notify - Notify class device and legacy sysfs interface
+ * platform_profile_notify - Notify the provider and aggregate sysfs interfaces
  * @dev: The class device
  */
 void platform_profile_notify(struct device *dev)
@@ -643,6 +644,7 @@ struct device *platform_profile_register(struct device *dev, const char *name,
 cleanup_cur:
 	scoped_guard(mutex, &profile_lock)
 		list_del(&to_pprof_handler(ppdev)->node);
+	profile_update();
 	device_unregister(ppdev);
 
 cleanup_ida:
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 4/5] platform profile: Document firmware-independent profile selection
  2026-10-19  7:39 [PATCH v2 0/5] Make platform profiles firmware independent Daniel Lezcano
                   ` (2 preceding siblings ...)
  2026-10-19  7:39 ` [PATCH v2 3/5] platform profile: Complete class notifications and registration cleanup Daniel Lezcano
@ 2026-10-19  7:39 ` Daniel Lezcano
  2026-10-19  7:39 ` [PATCH v2 5/5] platform profile: Make legacy ACPI aggregate interface optional Daniel Lezcano
  4 siblings, 0 replies; 9+ messages in thread
From: Daniel Lezcano @ 2026-10-19  7:39 UTC (permalink / raw)
  To: rafael
  Cc: Len Brown, Jonathan Corbet, Shuah Khan, Viresh Kumar,
	Hans de Goede, Ilpo Järvinen, Maximilian Luz,
	Shyam Sundar S K, Mark Pearson, Derek J. Clark, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Armin Wolf, Jai Luthra,
	Greg Kroah-Hartman, Mario Limonciello (AMD), Jack Wu,
	Mario Limonciello, Zhang DM, linux-acpi, linux-kernel, linux-doc,
	linux-pm, platform-driver-x86, Andrew Morton, Jakub Kicinski,
	Jonathan Cameron, Krzysztof Kozlowski

Describe the existing per-provider class devices as the firmware-neutral
userspace interface. Explain that providers can use any firmware transport
or implement profiles in software, without an ACPI companion or firmware
node on their parent device.

Retain the ACPI paths as compatibility interfaces for aggregate selection.
Clarify that non-ACPI userspace selects individual providers and document
the sequential application of aggregate writes and their partial-failure
behavior. No new userspace ABI is introduced.

Correct the per-provider choices attribute reference and extend existing
ACPI maintainer coverage to the extracted core and its interfaces.

Assisted-by: OpenCode:gpt-6.1-sol
Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
---
 .../ABI/testing/sysfs-class-platform-profile  |  2 +-
 .../userspace-api/sysfs-platform_profile.rst  | 46 ++++++++++++++-----
 MAINTAINERS                                   |  5 ++
 include/linux/platform_profile.h              |  3 ++
 4 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-class-platform-profile b/Documentation/ABI/testing/sysfs-class-platform-profile
index fcab26894ec3..75e0f6e2c128 100644
--- a/Documentation/ABI/testing/sysfs-class-platform-profile
+++ b/Documentation/ABI/testing/sysfs-class-platform-profile
@@ -35,7 +35,7 @@ Date:		March 2025
 KernelVersion:	6.14
 Description:	Reading this file gives the current selected profile for this
 		device. Writing this file with one of the strings from
-		platform_profile_choices changes the profile to the new value.
+		choices changes the profile to the new value.
 
 		This file can be monitored for changes by polling for POLLPRI,
 		POLLPRI will be signaled on any changes, independent of those
diff --git a/Documentation/userspace-api/sysfs-platform_profile.rst b/Documentation/userspace-api/sysfs-platform_profile.rst
index 6613e188242a..a85ede3fb8e5 100644
--- a/Documentation/userspace-api/sysfs-platform_profile.rst
+++ b/Documentation/userspace-api/sysfs-platform_profile.rst
@@ -1,6 +1,8 @@
-=====================================================================
-Platform Profile Selection (e.g. /sys/firmware/acpi/platform_profile)
-=====================================================================
+.. SPDX-License-Identifier: GPL-2.0
+
+==========================
+Platform Profile Selection
+==========================
 
 On modern systems the platform performance, temperature, fan and other
 hardware related characteristics are often dynamically configurable. The
@@ -15,6 +17,21 @@ operation or towards performance.
 The purpose of the platform_profile attribute is to offer a generic sysfs
 API for selecting the platform profile of these automatic mechanisms.
 
+The framework is independent of the firmware interface used by a provider.
+Drivers can implement profile selection using ACPI, an embedded controller,
+or another platform-specific mechanism. The core does not discover providers
+or interpret firmware descriptions.
+
+The firmware-independent userspace interface is available under
+``/sys/class/platform-profile/platform-profile-X/``, with ``name``,
+``choices`` and ``profile`` attributes for each registered provider.
+
+On ACPI systems, ``/sys/firmware/acpi/platform_profile_choices`` and
+``/sys/firmware/acpi/platform_profile`` remain available as compatibility
+interfaces for aggregate profile selection across registered providers.
+On non-ACPI systems, userspace selects profiles through the individual
+provider class devices.
+
 Note that this API is only for selecting the platform profile, it is
 NOT a goal of this API to allow monitoring the resulting performance
 characteristics. Monitoring performance is best done with device/vendor
@@ -52,13 +69,14 @@ Multiple driver support
 =======================
 When multiple drivers on a system advertise a platform profile handler, the
 platform profile handler core will only advertise the profiles that are
-common between all drivers to the ``/sys/firmware/acpi`` interfaces.
+common between all drivers to the ``/sys/firmware/acpi`` interfaces, when
+ACPI is active.
 
 This is to ensure there is no ambiguity on what the profile names mean when
 all handlers don't support a profile.
 
-Individual drivers will register a 'platform_profile' class device that has
-similar semantics as the ``/sys/firmware/acpi/platform_profile`` interface.
+Individual drivers register a ``platform-profile`` class device with
+``name``, ``choices`` and ``profile`` attributes.
 
 To discover which driver is associated with a platform profile handler the
 user can read the ``name`` attribute of the class device.
@@ -71,10 +89,14 @@ by writing to the ``profile`` attribute of the driver's class device.
 
 This will allow users to set different profiles for different drivers on the
 same system. If the selected profile by individual drivers differs the
-platform profile handler core will display the profile 'custom' to indicate
-that the profiles are not the same.
+ACPI aggregate interface will display the profile 'custom' to indicate that
+the profiles are not the same.
+
+While the ACPI ``platform_profile`` attribute has the value ``custom``,
+writing a common profile from ``platform_profile_choices`` to it will set
+the profile for all drivers. ``custom`` cannot be written to this aggregate
+interface.
 
-While the ``platform_profile`` attribute has the value ``custom``, writing a
-common profile from ``platform_profile_choices`` to the platform_profile
-attribute of the platform profile handler core will set the profile for all
-drivers.
+Aggregate profile changes are applied to providers sequentially. If a provider
+returns an error, providers updated earlier are not rolled back. Userspace can
+read the per-provider interfaces to determine their resulting state.
diff --git a/MAINTAINERS b/MAINTAINERS
index 8014b9f8253e..929e1e095f17 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -285,10 +285,14 @@ B:	https://bugzilla.kernel.org
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
 F:	Documentation/ABI/testing/configfs-acpi
 F:	Documentation/ABI/testing/sysfs-bus-acpi
+F:	Documentation/ABI/testing/sysfs-class-platform-profile
+F:	Documentation/ABI/testing/sysfs-platform_profile
 F:	Documentation/firmware-guide/acpi/
+F:	Documentation/userspace-api/sysfs-platform_profile.rst
 F:	arch/x86/kernel/acpi/
 F:	arch/x86/pci/acpi.c
 F:	drivers/acpi/
+F:	drivers/platform/profile/
 F:	drivers/pci/*/*acpi*
 F:	drivers/pci/*acpi*
 F:	drivers/pnp/pnpacpi/
@@ -296,6 +300,7 @@ F:	include/acpi/
 F:	include/linux/acpi.h
 F:	include/linux/fwnode.h
 F:	include/linux/fw_table.h
+F:	include/linux/platform_profile.h
 F:	lib/fw_table.c
 F:	rust/helpers/acpi.c
 F:	rust/kernel/acpi.rs
diff --git a/include/linux/platform_profile.h b/include/linux/platform_profile.h
index 855b28340e95..f23f9827eac6 100644
--- a/include/linux/platform_profile.h
+++ b/include/linux/platform_profile.h
@@ -2,6 +2,9 @@
 /*
  * Platform profile sysfs interface
  *
+ * Providers may use any firmware interface or implement profiles in software.
+ * The parent device does not need an ACPI companion or a firmware node.
+ *
  * See Documentation/userspace-api/sysfs-platform_profile.rst for more
  * information.
  */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 5/5] platform profile: Make legacy ACPI aggregate interface optional
  2026-10-19  7:39 [PATCH v2 0/5] Make platform profiles firmware independent Daniel Lezcano
                   ` (3 preceding siblings ...)
  2026-10-19  7:39 ` [PATCH v2 4/5] platform profile: Document firmware-independent profile selection Daniel Lezcano
@ 2026-10-19  7:39 ` Daniel Lezcano
  2026-10-19 19:02   ` Mario Limonciello
  4 siblings, 1 reply; 9+ messages in thread
From: Daniel Lezcano @ 2026-10-19  7:39 UTC (permalink / raw)
  To: rafael
  Cc: Len Brown, Jonathan Corbet, Shuah Khan, Viresh Kumar,
	Hans de Goede, Ilpo Järvinen, Maximilian Luz,
	Shyam Sundar S K, Mark Pearson, Derek J. Clark, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Armin Wolf, Jai Luthra,
	Greg Kroah-Hartman, Mario Limonciello (AMD), Jack Wu,
	Mario Limonciello, Zhang DM, linux-acpi, linux-kernel, linux-doc,
	linux-pm, platform-driver-x86, Andrew Morton, Jakub Kicinski,
	Jonathan Cameron, Krzysztof Kozlowski

The aggregate attributes under /sys/firmware/acpi/ are retained
for backwards compatibility. Firmware-independent userspace can already
discover providers through /sys/class/platform-profile/ and control each
provider using its choices and profile attributes.

Add PLATFORM_PROFILE_LEGACY, a boolean depending on PLATFORM_PROFILE and
ACPI, to control the legacy aggregate sysfs interface. Default it to y so
existing userspace continues to work when updating its configuration.

Compile the ACPI adapter and legacy sysfs callbacks only when the option
is enabled. Keep common aggregation helpers in the core because kernel
hotkey users still need platform_profile_cycle(). Class devices and their
notifications remain available regardless of the legacy option.

Document the compatibility option and recommend individual class devices
for new userspace. No daemon changes are included in this kernel series.
Move the legacy ABI documentation to obsolete/, retain its original
creation dates and attribute descriptions, identify the replacement class
attributes, and update its MAINTAINERS entry.

Mark the legacy ABI deprecated and document removal after the 2028 LTS
kernel, conditional on userspace migration. Keep the enabled default
until class-device support has been released and deployed in userspace.
Emit one shared pr_notice_once() when either legacy attribute is read or
the profile is written, identifying actual legacy use rather than warning
merely because the compatibility code was loaded.

Validate ARM64 without ACPI and x86 module builds with the option enabled
and disabled. With ACPI active and the option disabled, QEMU tests confirm
that the legacy attributes are absent while class controls, notifications,
kernel cycling and concurrent registration/removal continue to work.

Assisted-by: OpenCode:gpt-6.1-sol
Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
---
 .../{testing => obsolete}/sysfs-platform_profile   | 12 ++++
 .../userspace-api/sysfs-platform_profile.rst       | 17 +++--
 MAINTAINERS                                        |  2 +-
 drivers/platform/Kconfig                           | 23 ++++++-
 drivers/platform/profile/Makefile                  |  2 +-
 drivers/platform/profile/core.c                    | 72 ++++++++++++++++++----
 drivers/platform/profile/profile.h                 |  2 +-
 7 files changed, 109 insertions(+), 21 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-platform_profile b/Documentation/ABI/obsolete/sysfs-platform_profile
similarity index 70%
rename from Documentation/ABI/testing/sysfs-platform_profile
rename to Documentation/ABI/obsolete/sysfs-platform_profile
index 125324ab53a9..c001c8641a3a 100644
--- a/Documentation/ABI/testing/sysfs-platform_profile
+++ b/Documentation/ABI/obsolete/sysfs-platform_profile
@@ -2,6 +2,12 @@ What:		/sys/firmware/acpi/platform_profile_choices
 Date:		October 2020
 Contact:	Hans de Goede <hdegoede@redhat.com>
 Description:	This file contains a space-separated list of profiles supported for this device.
+		Available with CONFIG_PLATFORM_PROFILE_LEGACY on active ACPI
+		systems for backwards compatibility. New userspace should use
+		/sys/class/platform-profile/platform-profile-X/choices instead.
+		This interface is deprecated, with removal intended after the
+		2028 LTS kernel once userspace migration is complete. Accessing
+		either legacy attribute emits a one-time deprecation notice.
 
 		Drivers must use the following standard profile-names:
 
@@ -27,6 +33,12 @@ Description:	Reading this file gives the current selected profile for this
 		device. Writing this file with one of the strings from
 		platform_profile_choices changes the profile to the new value.
 
+		This legacy interface requires CONFIG_PLATFORM_PROFILE_LEGACY.
+		Individual class devices remain available without that option.
+		Use /sys/class/platform-profile/platform-profile-X/profile
+		instead. This interface is deprecated, with removal intended
+		after the 2028 LTS kernel once userspace migration is complete.
+
 		This file can be monitored for changes by polling for POLLPRI,
 		POLLPRI will be signalled on any changes, independent of those
 		changes coming from a userspace write; or coming from another
diff --git a/Documentation/userspace-api/sysfs-platform_profile.rst b/Documentation/userspace-api/sysfs-platform_profile.rst
index a85ede3fb8e5..a3fc1ee20832 100644
--- a/Documentation/userspace-api/sysfs-platform_profile.rst
+++ b/Documentation/userspace-api/sysfs-platform_profile.rst
@@ -26,11 +26,20 @@ The firmware-independent userspace interface is available under
 ``/sys/class/platform-profile/platform-profile-X/``, with ``name``,
 ``choices`` and ``profile`` attributes for each registered provider.
 
-On ACPI systems, ``/sys/firmware/acpi/platform_profile_choices`` and
+On ACPI systems with ``CONFIG_PLATFORM_PROFILE_LEGACY`` enabled,
+``/sys/firmware/acpi/platform_profile_choices`` and
 ``/sys/firmware/acpi/platform_profile`` remain available as compatibility
 interfaces for aggregate profile selection across registered providers.
-On non-ACPI systems, userspace selects profiles through the individual
-provider class devices.
+New userspace should select profiles through the individual provider class
+devices. These remain available when the legacy interface is disabled and
+on non-ACPI systems. Kernel profile cycling also remains available without
+the legacy interface.
+
+The legacy aggregate interface is deprecated. Removal is intended after the
+2028 LTS kernel, once userspace has migrated to released software using the
+class devices. Compatibility remains enabled by default during the migration;
+the date alone does not trigger removal. Accessing either legacy attribute
+emits a one-time kernel notice directing userspace to the class interface.
 
 Note that this API is only for selecting the platform profile, it is
 NOT a goal of this API to allow monitoring the resulting performance
@@ -70,7 +79,7 @@ Multiple driver support
 When multiple drivers on a system advertise a platform profile handler, the
 platform profile handler core will only advertise the profiles that are
 common between all drivers to the ``/sys/firmware/acpi`` interfaces, when
-ACPI is active.
+ACPI is active and ``CONFIG_PLATFORM_PROFILE_LEGACY`` is enabled.
 
 This is to ensure there is no ambiguity on what the profile names mean when
 all handlers don't support a profile.
diff --git a/MAINTAINERS b/MAINTAINERS
index 929e1e095f17..47b7724f20ea 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -283,10 +283,10 @@ S:	Supported
 Q:	https://patchwork.kernel.org/project/linux-acpi/list/
 B:	https://bugzilla.kernel.org
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
+F:	Documentation/ABI/obsolete/sysfs-platform_profile
 F:	Documentation/ABI/testing/configfs-acpi
 F:	Documentation/ABI/testing/sysfs-bus-acpi
 F:	Documentation/ABI/testing/sysfs-class-platform-profile
-F:	Documentation/ABI/testing/sysfs-platform_profile
 F:	Documentation/firmware-guide/acpi/
 F:	Documentation/userspace-api/sysfs-platform_profile.rst
 F:	arch/x86/kernel/acpi/
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 395b75f2edf0..7270f24a3b6e 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -9,12 +9,31 @@ config PLATFORM_PROFILE
 
 	  The core exposes per-provider controls under /sys/class/platform-profile/.
 	  A provider driver must register before profile attributes become
-	  available. ACPI is not required; on ACPI systems the legacy system-wide
-	  interface under /sys/firmware/acpi/ is also provided.
+	  available. ACPI is not required. The legacy system-wide interface under
+	  /sys/firmware/acpi/ can be enabled separately for existing userspace.
 
 	  To compile this as a module, choose M here: the module will be called
 	  platform_profile.
 
+config PLATFORM_PROFILE_LEGACY
+	bool "Legacy ACPI platform profile sysfs interface (DEPRECATED)"
+	depends on PLATFORM_PROFILE && ACPI
+	default y
+	help
+	  Expose platform_profile and platform_profile_choices under
+	  /sys/firmware/acpi/ for compatibility with existing userspace. These
+	  attributes aggregate the profiles of all registered providers.
+
+	  This interface is deprecated and is intended for removal after the
+	  2028 LTS kernel, once userspace has migrated to the class interface.
+	  It remains enabled by default until replacement userspace releases
+	  are available and have had time to reach users.
+
+	  New userspace should use the individual devices under
+	  /sys/class/platform-profile/ instead. Disabling this option removes
+	  the legacy sysfs interface but retains class devices and kernel
+	  profile cycling. If unsure, say Y to preserve compatibility.
+
 source "drivers/platform/mips/Kconfig"
 
 source "drivers/platform/loongarch/Kconfig"
diff --git a/drivers/platform/profile/Makefile b/drivers/platform/profile/Makefile
index ae95ba09383c..787fc9bc961a 100644
--- a/drivers/platform/profile/Makefile
+++ b/drivers/platform/profile/Makefile
@@ -2,4 +2,4 @@
 obj-$(CONFIG_PLATFORM_PROFILE) += platform_profile.o
 
 platform_profile-y := core.o
-platform_profile-$(CONFIG_ACPI) += acpi.o
+platform_profile-$(CONFIG_PLATFORM_PROFILE_LEGACY) += acpi.o
diff --git a/drivers/platform/profile/core.c b/drivers/platform/profile/core.c
index 320a5a964fea..1bffca947157 100644
--- a/drivers/platform/profile/core.c
+++ b/drivers/platform/profile/core.c
@@ -25,12 +25,16 @@ static DEFINE_MUTEX(profile_lock);
 /* Serialize registration and sysfs updates without blocking sysfs callbacks. */
 static DEFINE_MUTEX(profile_registration_lock);
 static LIST_HEAD(profile_handlers);
+#ifdef CONFIG_PLATFORM_PROFILE_LEGACY
 static struct kobject *compat_kobj;
+#endif
 
 static void profile_notify(void)
 {
+#ifdef CONFIG_PLATFORM_PROFILE_LEGACY
 	if (compat_kobj)
 		sysfs_notify(compat_kobj, NULL, "platform_profile");
+#endif
 }
 
 static int profile_update(void);
@@ -304,6 +308,12 @@ static int _aggregate_choices(struct device *dev, void *arg)
 	return 0;
 }
 
+#ifdef CONFIG_PLATFORM_PROFILE_LEGACY
+static void profile_legacy_notice(void)
+{
+	pr_notice_once("The ACPI platform_profile sysfs interface is deprecated and planned for removal after the 2028 LTS kernel once userspace has migrated; use /sys/class/platform-profile/ instead\n");
+}
+
 /**
  * _remove_hidden_choices - Remove hidden choices from aggregate data
  * @dev: The device
@@ -342,6 +352,8 @@ static ssize_t platform_profile_choices_show(struct kobject *kobj,
 	};
 	int err;
 
+	profile_legacy_notice();
+
 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
 		err = profile_walk_handlers(&data, _aggregate_choices);
 		if (err)
@@ -359,6 +371,7 @@ static ssize_t platform_profile_choices_show(struct kobject *kobj,
 
 	return _commmon_choices_show(data.aggregate, buf);
 }
+#endif
 
 /**
  * _aggregate_profiles - Aggregate the profiles across providers
@@ -403,6 +416,7 @@ static int _store_and_notify(struct device *dev, void *data)
 	return _notify_class_profile(dev, NULL);
 }
 
+#ifdef CONFIG_PLATFORM_PROFILE_LEGACY
 /**
  * platform_profile_show - Show the current aggregate profile
  * @kobj: The kobject
@@ -418,6 +432,8 @@ static ssize_t platform_profile_show(struct kobject *kobj,
 	enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
 	int err;
 
+	profile_legacy_notice();
+
 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
 		err = profile_walk_handlers(&profile, _aggregate_profiles);
 		if (err)
@@ -451,6 +467,8 @@ static ssize_t platform_profile_store(struct kobject *kobj,
 	int ret;
 	int i;
 
+	profile_legacy_notice();
+
 	/* Scan for a matching profile */
 	i = sysfs_match_string(profile_names, buf);
 	if (i < 0 || i == PLATFORM_PROFILE_CUSTOM)
@@ -502,6 +520,44 @@ static int profile_update(void)
 	return 0;
 }
 
+static int profile_compat_init(void)
+{
+	int err;
+
+	compat_kobj = platform_profile_compat_kobj();
+	if (!compat_kobj)
+		return 0;
+
+	err = sysfs_create_group(compat_kobj, &platform_profile_group);
+	if (err)
+		kobject_put(compat_kobj);
+
+	return err;
+}
+
+static void profile_compat_exit(void)
+{
+	if (compat_kobj) {
+		sysfs_remove_group(compat_kobj, &platform_profile_group);
+		kobject_put(compat_kobj);
+	}
+}
+#else
+static int profile_update(void)
+{
+	return 0;
+}
+
+static int profile_compat_init(void)
+{
+	return 0;
+}
+
+static void profile_compat_exit(void)
+{
+}
+#endif
+
 /**
  * platform_profile_notify - Notify the provider and aggregate sysfs interfaces
  * @dev: The class device
@@ -728,24 +784,16 @@ static int __init platform_profile_init(void)
 	if (err)
 		return err;
 
-	compat_kobj = platform_profile_compat_kobj();
-	if (compat_kobj) {
-		err = sysfs_create_group(compat_kobj, &platform_profile_group);
-		if (err) {
-			kobject_put(compat_kobj);
-			class_unregister(&platform_profile_class);
-		}
-	}
+	err = profile_compat_init();
+	if (err)
+		class_unregister(&platform_profile_class);
 
 	return err;
 }
 
 static void __exit platform_profile_exit(void)
 {
-	if (compat_kobj) {
-		sysfs_remove_group(compat_kobj, &platform_profile_group);
-		kobject_put(compat_kobj);
-	}
+	profile_compat_exit();
 	class_unregister(&platform_profile_class);
 }
 subsys_initcall_sync(platform_profile_init);
diff --git a/drivers/platform/profile/profile.h b/drivers/platform/profile/profile.h
index 910c9f7da6a0..362eb8cc4592 100644
--- a/drivers/platform/profile/profile.h
+++ b/drivers/platform/profile/profile.h
@@ -5,7 +5,7 @@
 #include <linux/kconfig.h>
 #include <linux/kobject.h>
 
-#ifdef CONFIG_ACPI
+#ifdef CONFIG_PLATFORM_PROFILE_LEGACY
 struct kobject *platform_profile_compat_kobj(void);
 #else
 static inline struct kobject *platform_profile_compat_kobj(void)

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/5] platform profile: Separate handler locking from sysfs lifetime
  2026-10-19  7:39 ` [PATCH v2 1/5] platform profile: Separate handler locking from sysfs lifetime Daniel Lezcano
@ 2026-10-19  8:50   ` Ilpo Järvinen
  2026-10-19 18:12     ` Rafael J. Wysocki (Intel)
  0 siblings, 1 reply; 9+ messages in thread
From: Ilpo Järvinen @ 2026-10-19  8:50 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Rafael J. Wysocki, Len Brown, Jonathan Corbet, Shuah Khan,
	Viresh Kumar, Hans de Goede, Maximilian Luz, Shyam Sundar S K,
	Mark Pearson, Derek J. Clark, Daniel Lezcano, Zhang Rui,
	Lukasz Luba, Armin Wolf, Jai Luthra, Greg Kroah-Hartman,
	Mario Limonciello (AMD), Jack Wu, Mario Limonciello, Zhang DM,
	linux-acpi, LKML, linux-doc, linux-pm, platform-driver-x86

On Mon, 19 Oct 2026, Daniel Lezcano wrote:

> Registration and removal currently hold profile_lock across
> device_unregister() and sysfs_update_group(). These operations can remove
> sysfs attributes and wait for their active callbacks to finish. The
> profile read and write callbacks acquire profile_lock themselves, so a
> concurrent access can deadlock:
> 
>   Removal/update path             Active sysfs callback
>   -------------------             ---------------------
>   acquire profile_lock            hold kernfs active reference
>   remove a sysfs attribute         wait for profile_lock
>   wait for its active references
> 
> The removal path cannot finish until the callback returns, and the
> callback cannot return until the removal path releases profile_lock.
> This applies both to per-provider device teardown and to updates of the
> legacy aggregate attribute group.
> 
> Commit 041142d7d252 ("ACPI: platform-profile: Fix possible deadlock in
> platform_profile_remove()") previously fixed this lock ordering by moving
> sysfs_remove_group() outside profile_lock. Commit 55b1e68134bf ("ACPI:
> platform_profile: Use guard(mutex) for register/unregister") reintroduced
> the issue by extending the mutex guard over sysfs_remove_group(). Later,
> commit 77be5cacb2c2 ("ACPI: platform_profile: Create class for ACPI
> platform profile") replaced that teardown with device_unregister() and
> sysfs_update_group(), still under the same mutex, extending the problem
> to class-device teardown and aggregate attribute updates.
> 
> Simply dropping profile_lock around these operations is insufficient.
> Aggregate operations currently discover providers through the class
> device list. A provider being unregistered could therefore still be
> selected for an aggregate read, write or cycle while its sysfs files are
> being torn down. Concurrent registrations and removals would also race
> when updating aggregate attribute visibility.
> 
> Introduce profile_registration_lock to serialize registration, removal
> and aggregate sysfs updates. Sysfs callbacks never acquire this mutex.
> Retain profile_lock for profile operations and use it to protect an
> explicit list of handlers participating in the aggregate interface.
> 
> Add a handler to that list only after device_register() succeeds. On
> removal, unlink it under profile_lock before calling device_unregister().
> Acquiring profile_lock first waits for any aggregate operation already
> using the handler to complete; unlinking prevents later aggregate
> operations from finding it. Release profile_lock before draining sysfs
> callbacks, allowing active per-provider callbacks to complete before the
> device is released. Apply the same unlink-before-unregister ordering to
> registration error cleanup.
> 
> Walk this list for aggregate reads, writes and profile cycling, and use
> its emptiness to determine aggregate attribute visibility. The class
> devices remain the per-provider userspace interface.
> 
> There can now be an interval between removing the last handler from the
> list and removing the aggregate attributes. Explicitly reject choices
> reads and writes during that interval: the intersection bitmap starts
> with every bit set, so an empty traversal must not advertise all profiles
> or report a successful write without updating a provider.
> 
> This separates aggregate membership from class device teardown while
> preserving the existing class and ACPI sysfs interfaces.
> 
> Fixes: 55b1e68134bf ("ACPI: platform_profile: Use guard(mutex) for register/unregister")
> Assisted-by: OpenCode:gpt-6.1-sol
> Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
> ---
>  drivers/acpi/platform_profile.c | 75 ++++++++++++++++++---------------
>  1 file changed, 42 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
> index 79d2e92e5c28..f98ce4cda02c 100644
> --- a/drivers/acpi/platform_profile.c
> +++ b/drivers/acpi/platform_profile.c
> @@ -8,6 +8,7 @@
>  #include <linux/bits.h>
>  #include <linux/cleanup.h>
>  #include <linux/init.h>
> +#include <linux/list.h>
>  #include <linux/mutex.h>
>  #include <linux/platform_profile.h>
>  #include <linux/sysfs.h>
> @@ -15,8 +16,12 @@
>  #define to_pprof_handler(d)	(container_of(d, struct platform_profile_handler, dev))
>  
>  static DEFINE_MUTEX(profile_lock);

Should definitely add comment to this lock as well to explain what it 
protects.

> +/* Serialize registration and sysfs updates without blocking sysfs callbacks. */
> +static DEFINE_MUTEX(profile_registration_lock);
> +static LIST_HEAD(profile_handlers);

-- 
 i.


>  struct platform_profile_handler {
> +	struct list_head node;
>  	const char *name;
>  	struct device dev;
>  	int minor;
> @@ -44,6 +49,22 @@ static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST);
>  
>  static DEFINE_IDA(platform_profile_ida);
>  
> +static int profile_walk_handlers(void *data,
> +				 int (*fn)(struct device *dev, void *data))
> +{
> +	struct platform_profile_handler *handler;
> +	int ret;
> +
> +	lockdep_assert_held(&profile_lock);
> +	list_for_each_entry(handler, &profile_handlers, node) {
> +		ret = fn(&handler->dev, data);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
>  /**
>   * _commmon_choices_show - Show the available profile choices
>   * @choices: The available profile choices
> @@ -306,20 +327,18 @@ static ssize_t platform_profile_choices_show(struct kobject *kobj,
>  	int err;
>  
>  	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
> -		err = class_for_each_device(&platform_profile_class, NULL,
> -					    &data, _aggregate_choices);
> +		err = profile_walk_handlers(&data, _aggregate_choices);
>  		if (err)
>  			return err;
>  		if (data.count == 1) {
> -			err = class_for_each_device(&platform_profile_class, NULL,
> -						    &data, _remove_hidden_choices);
> +			err = profile_walk_handlers(&data, _remove_hidden_choices);
>  			if (err)
>  				return err;
>  		}
>  	}
>  
>  	/* no profile handler registered any more */
> -	if (bitmap_empty(data.aggregate, PLATFORM_PROFILE_LAST))
> +	if (!data.count || bitmap_empty(data.aggregate, PLATFORM_PROFILE_LAST))
>  		return -EINVAL;
>  
>  	return _commmon_choices_show(data.aggregate, buf);
> @@ -384,8 +403,7 @@ static ssize_t platform_profile_show(struct kobject *kobj,
>  	int err;
>  
>  	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
> -		err = class_for_each_device(&platform_profile_class, NULL,
> -					    &profile, _aggregate_profiles);
> +		err = profile_walk_handlers(&profile, _aggregate_profiles);
>  		if (err)
>  			return err;
>  	}
> @@ -423,15 +441,13 @@ static ssize_t platform_profile_store(struct kobject *kobj,
>  		return -EINVAL;
>  
>  	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
> -		ret = class_for_each_device(&platform_profile_class, NULL,
> -					    &data, _aggregate_choices);
> +		ret = profile_walk_handlers(&data, _aggregate_choices);
>  		if (ret)
>  			return ret;
> -		if (!test_bit(i, data.aggregate))
> +		if (!data.count || !test_bit(i, data.aggregate))
>  			return -EOPNOTSUPP;
>  
> -		ret = class_for_each_device(&platform_profile_class, NULL, &i,
> -					    _store_and_notify);
> +		ret = profile_walk_handlers(&i, _store_and_notify);
>  		if (ret)
>  			return ret;
>  	}
> @@ -450,22 +466,11 @@ 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)
>  {
> -	struct device *dev;
> -
> -	dev = class_find_device(&platform_profile_class, NULL, NULL, profile_class_registered);
> -	if (!dev)
> -		return 0;
> -
> -	put_device(dev);
> +	guard(mutex)(&profile_lock);
>  
> -	return attr->mode;
> +	return list_empty(&profile_handlers) ? 0 : attr->mode;
>  }
>  
>  static const struct attribute_group platform_profile_group = {
> @@ -502,8 +507,7 @@ int platform_profile_cycle(void)
>  	int err;
>  
>  	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
> -		err = class_for_each_device(&platform_profile_class, NULL,
> -					    &profile, _aggregate_profiles);
> +		err = profile_walk_handlers(&profile, _aggregate_profiles);
>  		if (err)
>  			return err;
>  
> @@ -512,8 +516,7 @@ int platform_profile_cycle(void)
>  		    profile == PLATFORM_PROFILE_LAST)
>  			return -EINVAL;
>  
> -		err = class_for_each_device(&platform_profile_class, NULL,
> -					    &data, _aggregate_choices);
> +		err = profile_walk_handlers(&data, _aggregate_choices);
>  		if (err)
>  			return err;
>  
> @@ -525,8 +528,7 @@ int platform_profile_cycle(void)
>  					  PLATFORM_PROFILE_LAST,
>  					  profile + 1);
>  
> -		err = class_for_each_device(&platform_profile_class, NULL, &next,
> -					    _store_and_notify);
> +		err = profile_walk_handlers(&next, _store_and_notify);
>  
>  		if (err)
>  			return err;
> @@ -583,7 +585,7 @@ struct device *platform_profile_register(struct device *dev, const char *name,
>  		}
>  	}
>  
> -	guard(mutex)(&profile_lock);
> +	guard(mutex)(&profile_registration_lock);
>  
>  	/* create class interface for individual handler */
>  	minor = ida_alloc(&platform_profile_ida, GFP_KERNEL);
> @@ -604,6 +606,8 @@ struct device *platform_profile_register(struct device *dev, const char *name,
>  		put_device(ppdev);
>  		goto cleanup_ida;
>  	}
> +	scoped_guard(mutex, &profile_lock)
> +		list_add_tail(&to_pprof_handler(ppdev)->node, &profile_handlers);
>  
>  	sysfs_notify(acpi_kobj, NULL, "platform_profile");
>  
> @@ -614,6 +618,8 @@ struct device *platform_profile_register(struct device *dev, const char *name,
>  	return ppdev;
>  
>  cleanup_cur:
> +	scoped_guard(mutex, &profile_lock)
> +		list_del(&to_pprof_handler(ppdev)->node);
>  	device_unregister(ppdev);
>  
>  cleanup_ida:
> @@ -636,7 +642,10 @@ void platform_profile_remove(struct device *dev)
>  
>  	pprof = to_pprof_handler(dev);
>  
> -	guard(mutex)(&profile_lock);
> +	guard(mutex)(&profile_registration_lock);
> +
> +	scoped_guard(mutex, &profile_lock)
> +		list_del(&pprof->node);
>  
>  	ida_free(&platform_profile_ida, pprof->minor);
>  	device_unregister(&pprof->dev);
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/5] platform profile: Separate handler locking from sysfs lifetime
  2026-10-19  8:50   ` Ilpo Järvinen
@ 2026-10-19 18:12     ` Rafael J. Wysocki (Intel)
  0 siblings, 0 replies; 9+ messages in thread
From: Rafael J. Wysocki (Intel) @ 2026-10-19 18:12 UTC (permalink / raw)
  To: Ilpo Järvinen, Daniel Lezcano
  Cc: Viresh Kumar, Hans de Goede, Maximilian Luz, Shyam Sundar S K,
	Mark Pearson, Derek J. Clark, Daniel Lezcano, Zhang Rui,
	Lukasz Luba, Armin Wolf, Jai Luthra, Greg Kroah-Hartman,
	Mario Limonciello (AMD), Jack Wu, Mario Limonciello, Zhang DM,
	linux-acpi, LKML, linux-doc, linux-pm, platform-driver-x86

On Mon, Oct 19, 2026 at 10:51 AM Ilpo Järvinen
<ilpo.jarvinen@linux.intel.com> wrote:
>
> On Mon, 19 Oct 2026, Daniel Lezcano wrote:
>
> > Registration and removal currently hold profile_lock across
> > device_unregister() and sysfs_update_group(). These operations can remove
> > sysfs attributes and wait for their active callbacks to finish. The
> > profile read and write callbacks acquire profile_lock themselves, so a
> > concurrent access can deadlock:
> >
> >   Removal/update path             Active sysfs callback
> >   -------------------             ---------------------
> >   acquire profile_lock            hold kernfs active reference
> >   remove a sysfs attribute         wait for profile_lock
> >   wait for its active references
> >
> > The removal path cannot finish until the callback returns, and the
> > callback cannot return until the removal path releases profile_lock.
> > This applies both to per-provider device teardown and to updates of the
> > legacy aggregate attribute group.
> >
> > Commit 041142d7d252 ("ACPI: platform-profile: Fix possible deadlock in
> > platform_profile_remove()") previously fixed this lock ordering by moving
> > sysfs_remove_group() outside profile_lock. Commit 55b1e68134bf ("ACPI:
> > platform_profile: Use guard(mutex) for register/unregister") reintroduced
> > the issue by extending the mutex guard over sysfs_remove_group(). Later,
> > commit 77be5cacb2c2 ("ACPI: platform_profile: Create class for ACPI
> > platform profile") replaced that teardown with device_unregister() and
> > sysfs_update_group(), still under the same mutex, extending the problem
> > to class-device teardown and aggregate attribute updates.
> >
> > Simply dropping profile_lock around these operations is insufficient.
> > Aggregate operations currently discover providers through the class
> > device list. A provider being unregistered could therefore still be
> > selected for an aggregate read, write or cycle while its sysfs files are
> > being torn down. Concurrent registrations and removals would also race
> > when updating aggregate attribute visibility.
> >
> > Introduce profile_registration_lock to serialize registration, removal
> > and aggregate sysfs updates. Sysfs callbacks never acquire this mutex.
> > Retain profile_lock for profile operations and use it to protect an
> > explicit list of handlers participating in the aggregate interface.
> >
> > Add a handler to that list only after device_register() succeeds. On
> > removal, unlink it under profile_lock before calling device_unregister().
> > Acquiring profile_lock first waits for any aggregate operation already
> > using the handler to complete; unlinking prevents later aggregate
> > operations from finding it. Release profile_lock before draining sysfs
> > callbacks, allowing active per-provider callbacks to complete before the
> > device is released. Apply the same unlink-before-unregister ordering to
> > registration error cleanup.
> >
> > Walk this list for aggregate reads, writes and profile cycling, and use
> > its emptiness to determine aggregate attribute visibility. The class
> > devices remain the per-provider userspace interface.
> >
> > There can now be an interval between removing the last handler from the
> > list and removing the aggregate attributes. Explicitly reject choices
> > reads and writes during that interval: the intersection bitmap starts
> > with every bit set, so an empty traversal must not advertise all profiles
> > or report a successful write without updating a provider.
> >
> > This separates aggregate membership from class device teardown while
> > preserving the existing class and ACPI sysfs interfaces.
> >
> > Fixes: 55b1e68134bf ("ACPI: platform_profile: Use guard(mutex) for register/unregister")
> > Assisted-by: OpenCode:gpt-6.1-sol
> > Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
> > ---
> >  drivers/acpi/platform_profile.c | 75 ++++++++++++++++++---------------
> >  1 file changed, 42 insertions(+), 33 deletions(-)
> >
> > diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
> > index 79d2e92e5c28..f98ce4cda02c 100644
> > --- a/drivers/acpi/platform_profile.c
> > +++ b/drivers/acpi/platform_profile.c
> > @@ -8,6 +8,7 @@
> >  #include <linux/bits.h>
> >  #include <linux/cleanup.h>
> >  #include <linux/init.h>
> > +#include <linux/list.h>
> >  #include <linux/mutex.h>
> >  #include <linux/platform_profile.h>
> >  #include <linux/sysfs.h>
> > @@ -15,8 +16,12 @@
> >  #define to_pprof_handler(d)  (container_of(d, struct platform_profile_handler, dev))
> >
> >  static DEFINE_MUTEX(profile_lock);
>
> Should definitely add comment to this lock as well to explain what it
> protects.

+1

> > +/* Serialize registration and sysfs updates without blocking sysfs callbacks. */
> > +static DEFINE_MUTEX(profile_registration_lock);
> > +static LIST_HEAD(profile_handlers);

Also, I think that the rest of the series is not going to get into 7.4
in any shape or form, so my suggestion would be to address the above
and send a new version of the $subject patch as a separate fix, and
defer the other patches in the series until 7.4-rc1 is out.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 5/5] platform profile: Make legacy ACPI aggregate interface optional
  2026-10-19  7:39 ` [PATCH v2 5/5] platform profile: Make legacy ACPI aggregate interface optional Daniel Lezcano
@ 2026-10-19 19:02   ` Mario Limonciello
  0 siblings, 0 replies; 9+ messages in thread
From: Mario Limonciello @ 2026-10-19 19:02 UTC (permalink / raw)
  To: Daniel Lezcano, rafael
  Cc: Len Brown, Jonathan Corbet, Shuah Khan, Viresh Kumar,
	Hans de Goede, Ilpo Järvinen, Maximilian Luz,
	Shyam Sundar S K, Mark Pearson, Derek J. Clark, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Armin Wolf, Jai Luthra,
	Greg Kroah-Hartman, Jack Wu, Mario Limonciello, Zhang DM,
	linux-acpi, linux-kernel, linux-doc, linux-pm,
	platform-driver-x86, Andrew Morton, Jakub Kicinski,
	Jonathan Cameron, Krzysztof Kozlowski

On 10/19/26 02:39, Daniel Lezcano wrote:
> The aggregate attributes under /sys/firmware/acpi/ are retained
> for backwards compatibility. Firmware-independent userspace can already
> discover providers through /sys/class/platform-profile/ and control each
> provider using its choices and profile attributes.
> 
> Add PLATFORM_PROFILE_LEGACY, a boolean depending on PLATFORM_PROFILE and
> ACPI, to control the legacy aggregate sysfs interface. Default it to y so
> existing userspace continues to work when updating its configuration.
> 
> Compile the ACPI adapter and legacy sysfs callbacks only when the option
> is enabled. Keep common aggregation helpers in the core because kernel
> hotkey users still need platform_profile_cycle(). Class devices and their
> notifications remain available regardless of the legacy option.
> 
> Document the compatibility option and recommend individual class devices
> for new userspace. No daemon changes are included in this kernel series.
> Move the legacy ABI documentation to obsolete/, retain its original
> creation dates and attribute descriptions, identify the replacement class
> attributes, and update its MAINTAINERS entry.
> 
> Mark the legacy ABI deprecated and document removal after the 2028 LTS
> kernel, conditional on userspace migration. Keep the enabled default
> until class-device support has been released and deployed in userspace.
> Emit one shared pr_notice_once() when either legacy attribute is read or
> the profile is written, identifying actual legacy use rather than warning
> merely because the compatibility code was loaded.
> 
> Validate ARM64 without ACPI and x86 module builds with the option enabled
> and disabled. With ACPI active and the option disabled, QEMU tests confirm
> that the legacy attributes are absent while class controls, notifications,
> kernel cycling and concurrent registration/removal continue to work.
> 
> Assisted-by: OpenCode:gpt-6.1-sol
> Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
> ---
>   .../{testing => obsolete}/sysfs-platform_profile   | 12 ++++
>   .../userspace-api/sysfs-platform_profile.rst       | 17 +++--
>   MAINTAINERS                                        |  2 +-
>   drivers/platform/Kconfig                           | 23 ++++++-
>   drivers/platform/profile/Makefile                  |  2 +-
>   drivers/platform/profile/core.c                    | 72 ++++++++++++++++++----
>   drivers/platform/profile/profile.h                 |  2 +-
>   7 files changed, 109 insertions(+), 21 deletions(-)
> 
> diff --git a/Documentation/ABI/testing/sysfs-platform_profile b/Documentation/ABI/obsolete/sysfs-platform_profile
> similarity index 70%
> rename from Documentation/ABI/testing/sysfs-platform_profile
> rename to Documentation/ABI/obsolete/sysfs-platform_profile
> index 125324ab53a9..c001c8641a3a 100644
> --- a/Documentation/ABI/testing/sysfs-platform_profile
> +++ b/Documentation/ABI/obsolete/sysfs-platform_profile
> @@ -2,6 +2,12 @@ What:		/sys/firmware/acpi/platform_profile_choices
>   Date:		October 2020
>   Contact:	Hans de Goede <hdegoede@redhat.com>
>   Description:	This file contains a space-separated list of profiles supported for this device.
> +		Available with CONFIG_PLATFORM_PROFILE_LEGACY on active ACPI
> +		systems for backwards compatibility. New userspace should use
I don't think "new" vs old is a good distinction for documentation.

Just say "Userspace should use"

> +		/sys/class/platform-profile/platform-profile-X/choices instead.
> +		This interface is deprecated, with removal intended after the
> +		2028 LTS kernel once userspace migration is complete. Accessing
> +		either legacy attribute emits a one-time deprecation notice.
>   
>   		Drivers must use the following standard profile-names:
>   
> @@ -27,6 +33,12 @@ Description:	Reading this file gives the current selected profile for this
>   		device. Writing this file with one of the strings from
>   		platform_profile_choices changes the profile to the new value.
>   
> +		This legacy interface requires CONFIG_PLATFORM_PROFILE_LEGACY.
> +		Individual class devices remain available without that option.
> +		Use /sys/class/platform-profile/platform-profile-X/profile
> +		instead. This interface is deprecated, with removal intended
> +		after the 2028 LTS kernel once userspace migration is complete.
> +
>   		This file can be monitored for changes by polling for POLLPRI,
>   		POLLPRI will be signalled on any changes, independent of those
>   		changes coming from a userspace write; or coming from another
> diff --git a/Documentation/userspace-api/sysfs-platform_profile.rst b/Documentation/userspace-api/sysfs-platform_profile.rst
> index a85ede3fb8e5..a3fc1ee20832 100644
> --- a/Documentation/userspace-api/sysfs-platform_profile.rst
> +++ b/Documentation/userspace-api/sysfs-platform_profile.rst
> @@ -26,11 +26,20 @@ The firmware-independent userspace interface is available under
>   ``/sys/class/platform-profile/platform-profile-X/``, with ``name``,
>   ``choices`` and ``profile`` attributes for each registered provider.
>   
> -On ACPI systems, ``/sys/firmware/acpi/platform_profile_choices`` and
> +On ACPI systems with ``CONFIG_PLATFORM_PROFILE_LEGACY`` enabled,
> +``/sys/firmware/acpi/platform_profile_choices`` and
>   ``/sys/firmware/acpi/platform_profile`` remain available as compatibility
>   interfaces for aggregate profile selection across registered providers.
> -On non-ACPI systems, userspace selects profiles through the individual
> -provider class devices.
> +New userspace should select profiles through the individual provider class
Similar comment.> +devices. These remain available when the legacy 
interface is disabled and
> +on non-ACPI systems. Kernel profile cycling also remains available without
> +the legacy interface.
> +
> +The legacy aggregate interface is deprecated. Removal is intended after the
> +2028 LTS kernel, once userspace has migrated to released software using the
> +class devices. Compatibility remains enabled by default during the migration;
> +the date alone does not trigger removal. Accessing either legacy attribute
> +emits a one-time kernel notice directing userspace to the class interface.
>   
>   Note that this API is only for selecting the platform profile, it is
>   NOT a goal of this API to allow monitoring the resulting performance
> @@ -70,7 +79,7 @@ Multiple driver support
>   When multiple drivers on a system advertise a platform profile handler, the
>   platform profile handler core will only advertise the profiles that are
>   common between all drivers to the ``/sys/firmware/acpi`` interfaces, when
> -ACPI is active.
> +ACPI is active and ``CONFIG_PLATFORM_PROFILE_LEGACY`` is enabled.
>   
>   This is to ensure there is no ambiguity on what the profile names mean when
>   all handlers don't support a profile.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 929e1e095f17..47b7724f20ea 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -283,10 +283,10 @@ S:	Supported
>   Q:	https://patchwork.kernel.org/project/linux-acpi/list/
>   B:	https://bugzilla.kernel.org
>   T:	git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
> +F:	Documentation/ABI/obsolete/sysfs-platform_profile
>   F:	Documentation/ABI/testing/configfs-acpi
>   F:	Documentation/ABI/testing/sysfs-bus-acpi
>   F:	Documentation/ABI/testing/sysfs-class-platform-profile
> -F:	Documentation/ABI/testing/sysfs-platform_profile
>   F:	Documentation/firmware-guide/acpi/
>   F:	Documentation/userspace-api/sysfs-platform_profile.rst
>   F:	arch/x86/kernel/acpi/
> diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
> index 395b75f2edf0..7270f24a3b6e 100644
> --- a/drivers/platform/Kconfig
> +++ b/drivers/platform/Kconfig
> @@ -9,12 +9,31 @@ config PLATFORM_PROFILE
>   
>   	  The core exposes per-provider controls under /sys/class/platform-profile/.
>   	  A provider driver must register before profile attributes become
> -	  available. ACPI is not required; on ACPI systems the legacy system-wide
> -	  interface under /sys/firmware/acpi/ is also provided.
> +	  available. ACPI is not required. The legacy system-wide interface under
> +	  /sys/firmware/acpi/ can be enabled separately for existing userspace.
>   
>   	  To compile this as a module, choose M here: the module will be called
>   	  platform_profile.
>   
> +config PLATFORM_PROFILE_LEGACY
> +	bool "Legacy ACPI platform profile sysfs interface (DEPRECATED)"
> +	depends on PLATFORM_PROFILE && ACPI
> +	default y
> +	help
> +	  Expose platform_profile and platform_profile_choices under
> +	  /sys/firmware/acpi/ for compatibility with existing userspace. These
> +	  attributes aggregate the profiles of all registered providers.
> +
> +	  This interface is deprecated and is intended for removal after the
> +	  2028 LTS kernel, once userspace has migrated to the class interface.
> +	  It remains enabled by default until replacement userspace releases
> +	  are available and have had time to reach users.
> +
> +	  New userspace should use the individual devices under
> +	  /sys/class/platform-profile/ instead. Disabling this option removes
> +	  the legacy sysfs interface but retains class devices and kernel
> +	  profile cycling. If unsure, say Y to preserve compatibility.
> +
>   source "drivers/platform/mips/Kconfig"
>   
>   source "drivers/platform/loongarch/Kconfig"
> diff --git a/drivers/platform/profile/Makefile b/drivers/platform/profile/Makefile
> index ae95ba09383c..787fc9bc961a 100644
> --- a/drivers/platform/profile/Makefile
> +++ b/drivers/platform/profile/Makefile
> @@ -2,4 +2,4 @@
>   obj-$(CONFIG_PLATFORM_PROFILE) += platform_profile.o
>   
>   platform_profile-y := core.o
> -platform_profile-$(CONFIG_ACPI) += acpi.o
> +platform_profile-$(CONFIG_PLATFORM_PROFILE_LEGACY) += acpi.o
> diff --git a/drivers/platform/profile/core.c b/drivers/platform/profile/core.c
> index 320a5a964fea..1bffca947157 100644
> --- a/drivers/platform/profile/core.c
> +++ b/drivers/platform/profile/core.c
> @@ -25,12 +25,16 @@ static DEFINE_MUTEX(profile_lock);
>   /* Serialize registration and sysfs updates without blocking sysfs callbacks. */
>   static DEFINE_MUTEX(profile_registration_lock);
>   static LIST_HEAD(profile_handlers);
> +#ifdef CONFIG_PLATFORM_PROFILE_LEGACY
>   static struct kobject *compat_kobj;
> +#endif
>   
>   static void profile_notify(void)
>   {
> +#ifdef CONFIG_PLATFORM_PROFILE_LEGACY
>   	if (compat_kobj)
>   		sysfs_notify(compat_kobj, NULL, "platform_profile");
> +#endif
>   }
>   
>   static int profile_update(void);
> @@ -304,6 +308,12 @@ static int _aggregate_choices(struct device *dev, void *arg)
>   	return 0;
>   }
>   
> +#ifdef CONFIG_PLATFORM_PROFILE_LEGACY
> +static void profile_legacy_notice(void)
> +{
> +	pr_notice_once("The ACPI platform_profile sysfs interface is deprecated and planned for removal after the 2028 LTS kernel once userspace has migrated; use /sys/class/platform-profile/ instead\n");

I would just leave userspace out.  Maybe just:

"The ACPI platform_profile sysfs interface is deprecated with a planned 
removal in 2028".

> +}
> +
>   /**
>    * _remove_hidden_choices - Remove hidden choices from aggregate data
>    * @dev: The device
> @@ -342,6 +352,8 @@ static ssize_t platform_profile_choices_show(struct kobject *kobj,
>   	};
>   	int err;
>   
> +	profile_legacy_notice();
> +
>   	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
>   		err = profile_walk_handlers(&data, _aggregate_choices);
>   		if (err)
> @@ -359,6 +371,7 @@ static ssize_t platform_profile_choices_show(struct kobject *kobj,
>   
>   	return _commmon_choices_show(data.aggregate, buf);
>   }
> +#endif
>   
>   /**
>    * _aggregate_profiles - Aggregate the profiles across providers
> @@ -403,6 +416,7 @@ static int _store_and_notify(struct device *dev, void *data)
>   	return _notify_class_profile(dev, NULL);
>   }
>   
> +#ifdef CONFIG_PLATFORM_PROFILE_LEGACY
>   /**
>    * platform_profile_show - Show the current aggregate profile
>    * @kobj: The kobject
> @@ -418,6 +432,8 @@ static ssize_t platform_profile_show(struct kobject *kobj,
>   	enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
>   	int err;
>   
> +	profile_legacy_notice();
> +
>   	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
>   		err = profile_walk_handlers(&profile, _aggregate_profiles);
>   		if (err)
> @@ -451,6 +467,8 @@ static ssize_t platform_profile_store(struct kobject *kobj,
>   	int ret;
>   	int i;
>   
> +	profile_legacy_notice();
> +
>   	/* Scan for a matching profile */
>   	i = sysfs_match_string(profile_names, buf);
>   	if (i < 0 || i == PLATFORM_PROFILE_CUSTOM)
> @@ -502,6 +520,44 @@ static int profile_update(void)
>   	return 0;
>   }
>   
> +static int profile_compat_init(void)
> +{
> +	int err;
> +
> +	compat_kobj = platform_profile_compat_kobj();
> +	if (!compat_kobj)
> +		return 0;
> +
> +	err = sysfs_create_group(compat_kobj, &platform_profile_group);
> +	if (err)
> +		kobject_put(compat_kobj);
> +
> +	return err;
> +}
> +
> +static void profile_compat_exit(void)
> +{
> +	if (compat_kobj) {
> +		sysfs_remove_group(compat_kobj, &platform_profile_group);
> +		kobject_put(compat_kobj);
> +	}
> +}
> +#else
> +static int profile_update(void)
> +{
> +	return 0;
> +}
> +
> +static int profile_compat_init(void)
> +{
> +	return 0;
> +}
> +
> +static void profile_compat_exit(void)
> +{
> +}
> +#endif
> +
>   /**
>    * platform_profile_notify - Notify the provider and aggregate sysfs interfaces
>    * @dev: The class device
> @@ -728,24 +784,16 @@ static int __init platform_profile_init(void)
>   	if (err)
>   		return err;
>   
> -	compat_kobj = platform_profile_compat_kobj();
> -	if (compat_kobj) {
> -		err = sysfs_create_group(compat_kobj, &platform_profile_group);
> -		if (err) {
> -			kobject_put(compat_kobj);
> -			class_unregister(&platform_profile_class);
> -		}
> -	}
> +	err = profile_compat_init();
> +	if (err)
> +		class_unregister(&platform_profile_class);
>   
>   	return err;
>   }
>   
>   static void __exit platform_profile_exit(void)
>   {
> -	if (compat_kobj) {
> -		sysfs_remove_group(compat_kobj, &platform_profile_group);
> -		kobject_put(compat_kobj);
> -	}
> +	profile_compat_exit();
>   	class_unregister(&platform_profile_class);
>   }
>   subsys_initcall_sync(platform_profile_init);
> diff --git a/drivers/platform/profile/profile.h b/drivers/platform/profile/profile.h
> index 910c9f7da6a0..362eb8cc4592 100644
> --- a/drivers/platform/profile/profile.h
> +++ b/drivers/platform/profile/profile.h
> @@ -5,7 +5,7 @@
>   #include <linux/kconfig.h>
>   #include <linux/kobject.h>
>   
> -#ifdef CONFIG_ACPI
> +#ifdef CONFIG_PLATFORM_PROFILE_LEGACY
>   struct kobject *platform_profile_compat_kobj(void);
>   #else
>   static inline struct kobject *platform_profile_compat_kobj(void)


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-10-19 19:02 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19  7:39 [PATCH v2 0/5] Make platform profiles firmware independent Daniel Lezcano
2026-10-19  7:39 ` [PATCH v2 1/5] platform profile: Separate handler locking from sysfs lifetime Daniel Lezcano
2026-10-19  8:50   ` Ilpo Järvinen
2026-10-19 18:12     ` Rafael J. Wysocki (Intel)
2026-10-19  7:39 ` [PATCH v2 2/5] platform profile: Extract the core from ACPI Daniel Lezcano
2026-10-19  7:39 ` [PATCH v2 3/5] platform profile: Complete class notifications and registration cleanup Daniel Lezcano
2026-10-19  7:39 ` [PATCH v2 4/5] platform profile: Document firmware-independent profile selection Daniel Lezcano
2026-10-19  7:39 ` [PATCH v2 5/5] platform profile: Make legacy ACPI aggregate interface optional Daniel Lezcano
2026-10-19 19:02   ` Mario Limonciello

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox