Re: [PATCH v3 2/2] hwmon: acpi_power_meter: convert to hwmon_device_register_with_info

From: LABBE Corentin
Date: Sat May 21 2022 - 15:16:42 EST


Le Sat, May 21, 2022 at 06:52:15AM -0700, Guenter Roeck a écrit :
> On 5/16/22 05:21, Guenter Roeck wrote:
> > On 5/15/22 23:21, LABBE Corentin wrote:
> >> Le Sun, May 15, 2022 at 05:29:54PM -0700, Guenter Roeck a écrit :
> >>> On 5/15/22 12:36, LABBE Corentin wrote:
> >>>> Le Wed, May 11, 2022 at 07:10:29PM -0700, Guenter Roeck a écrit :
> >>>>> Corentin,
> >>>>>
> >>>>> On 5/8/22 23:30, Corentin Labbe wrote:
> >>>>>> Booting lead to a hwmon_device_register() is deprecated. Please convert the driver to use hwmon_device_register_with_info().
> >>>>>> So let's convert the driver to use hwmon_device_register_with_info().
> >>>>>>
> >>>>>> Signed-off-by: Corentin Labbe <clabbe@xxxxxxxxxxxx>
> >>>>>> ---
> >>>>> [ ... ]
> >>>>>
> >>>>>> @@ -836,20 +740,20 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event)
> >>>>>>             if (res)
> >>>>>>                 break;
> >>>>>> -        remove_attrs(resource);
> >>>>>> +        remove_domain_devices(resource);
> >>>>>>             setup_attrs(resource);
> >>>>>
> >>>>> Zhang Rui found an interesting problem with this code:
> >>>>> It needs a call to sysfs_update_groups(hwmon_dev->groups)
> >>>>> to update sysfs attribute visibility, probably between
> >>>>> remove_domain_devices() and setup_attrs().
> >>>>>
> >>>>>>             break;
> >>>>>>         case METER_NOTIFY_TRIP:
> >>>>>> -        sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME);
> >>>>>> +        hwmon_notify_event(&device->dev, hwmon_power, hwmon_power_average, 0);
> >>>>>
> >>>>> ... which makes realize: The notification device should be the hwmon device.
> >>>>> That would be resource->hwmon_dev, not the acpi device.
> >>>>>
> >>>>
> >>>> Hello
> >>>>
> >>>> Since my hardware lacks capabilities testing this, I have emulated it on qemu:
> >>>> https://github.com/montjoie/qemu/commit/320f2ddacb954ab308ef699f66fca6313f75bc2b
> >>>>
> >>>> I have added a custom ACPI _DBX method for triggering some ACPI state change. (like config change, like enabling CAP).
> >>>>
> >>>> For testing config change I have tried lot of way:
> >>>>                   res = read_capabilities(resource);
> >>>> @@ -742,18 +758,22 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event)
> >>>>                   remove_domain_devices(resource);
> >>>>                   setup_attrs(resource);
> >>>> +               res = sysfs_update_groups(&resource->hwmon_dev->kobj, acpi_power_groups);
> >>>> +               res = sysfs_update_groups(&resource->acpi_dev->dev.kobj, acpi_power_groups);
> >>>> +               res = hwmon_notify_event(resource->hwmon_dev, hwmon_power, hwmon_power_cap, 0);
> >>>> +               res = hwmon_notify_event(resource->hwmon_dev, hwmon_power, hwmon_power_average, 0);
> >>>
> >>> Did you add a debug log here ?
> >>
> >> Yes I added debug log to check what is called.
> >>
> >>>
> >>> acpi_power_groups would be the wrong parameter for sysfs_update_groups().
> >>> It would have to be resource->hwmon_dev->groups.
> >>>
> >>
> >> Even with that, no call to is_visible:
> >> @@ -742,18 +758,22 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event)
> >>                  remove_domain_devices(resource);
> >>                  setup_attrs(resource);
> >> +               res = sysfs_update_groups(&resource->hwmon_dev->kobj, resource->hwmon_dev->groups);
> >> +               res = sysfs_update_groups(&resource->acpi_dev->dev.kobj, resource->hwmon_dev->groups);
> >> +               res = hwmon_notify_event(resource->hwmon_dev, hwmon_power, hwmon_power_cap, 0);
> >> +               res = hwmon_notify_event(resource->hwmon_dev, hwmon_power, hwmon_power_average, 0);
> >>                  break;
> >>
> >> I checked drivers/hwmon/hwmon.c is seems that is_visible is only called by gen_attr/gen_attrs which is only called by __hwmon_create_attrs and then by registers functions.
> >> So perhaps it explain why it is never called.
> >
> > Ah yes, you are correct. Sorry, it has been too long ago that I wrote that code.
> > Effectively that means we'll have to rework the hwmon core to generate attributes
> > anyway and leave it up to the driver core to call the is_visible function.
> >
>
> Attached is an outline of what would be needed in the hwmon core.
> Completely untested. I wonder if it may be easier to always
> create all attributes and have them return -ENODATA if not
> supported.
>

With your patch, the notify config change lead to new attributes to be displayed.

Your patch just needs:
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -315,7 +315,7 @@ static void hwmon_thermal_notify(struct device *dev, int index)
}

#else
-static int hwmon_thermal_register_sensors(struct device *dev)
+static int hwmon_thermal_register_sensors(struct device *dev, bool update)
{
return 0;
}

Tested with:
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -40,7 +40,7 @@
#define METER_NOTIFY_INTERVAL 0x84

static int cap_in_hardware;
-static bool force_cap_on;
+static bool force_cap_on = 1;

static int can_cap_in_hardware(void)
{
@@ -337,6 +337,16 @@ static ssize_t power1_is_battery_show(struct device *dev,
{
struct acpi_power_meter_resource *resource = dev_get_drvdata(dev);
int val;
+ unsigned long long data;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(resource->acpi_dev->handle, "_DBX",
+ NULL, &data);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_DBX",
+ status);
+ }
+ pr_info("DBX give %llu\n", data);

if (resource->caps.flags & POWER_METER_IS_BATTERY)
val = 1;
@@ -570,6 +580,8 @@ static umode_t acpi_power_is_visible(const void *data,
{
const struct acpi_power_meter_resource *resource = data;

+ pr_info("%s\n", __func__);
+
switch (attr) {
case hwmon_power_average_min:
case hwmon_power_average_max:
@@ -741,7 +753,7 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event)

remove_domain_devices(resource);
setup_attrs(resource);
- res = sysfs_update_groups(&resource->acpi_dev->dev.kobj, resource->hwmon_dev->groups);
+ res = hwmon_update_groups(resource->hwmon_dev);
break;
case METER_NOTIFY_TRIP:
On a microvm qemu with my ACPI power meter emulation:
/ # ls /sys/class/hwmon/hwmon0/
device power1_average power1_is_battery subsystem
name power1_average_interval power1_model_number uevent
power power1_interval_max power1_oem_info
power1_accuracy power1_interval_min power1_serial_number
/ # cat /sys/class/hwmon/hwmon0/
device/ power1_average_interval power1_oem_info
name power1_interval_max power1_serial_number
power/ power1_interval_min subsystem/
power1_accuracy power1_is_battery uevent
power1_average power1_model_number
/ # cat /sys/class/hwmon/hwmon0/power1_is_battery
[ 14.969697] power_meter ACPI000D:00: Found ACPI power meter.
[ 14.970114] acpi_power_is_visible
[ 14.970133] acpi_power_is_visible
[ 14.970141] acpi_power_is_visible
[ 14.970147] acpi_power_is_visible
[ 14.970153] acpi_power_is_visible
[ 14.970158] acpi_power_is_visible
[ 14.970164] acpi_power_is_visible
[ 14.970169] acpi_power_is_visible
[ 14.970187] acpi_power_is_visible
[ 14.970193] acpi_power_is_visible
[ 14.970202] acpi_power_is_visible
[ 14.970346] DBX give 40
[ 14.975185] power_meter ACPI000D:00: Capping in progress.
0
cat: /sys/class/hwmon/hwmon0/power1_is_battery: No such device
/ # ls /sys/class/hwmon/hwmon0/
device power1_average_min power1_is_battery
name power1_cap power1_model_number
power power1_cap_hyst power1_oem_info
power1_accuracy power1_cap_max power1_serial_number
power1_average power1_cap_min subsystem
power1_average_interval power1_interval_max uevent
power1_average_max power1_interval_min

Thanks