X86 platform drivers
 help / color / mirror / Atom feed
* [PATCH] platform/x86: dell-ddv: Fix taking the psy->extensions_sem lock twice
@ 2025-06-20 17:58 Hans de Goede
  2025-06-22 18:41 ` Armin Wolf
  0 siblings, 1 reply; 5+ messages in thread
From: Hans de Goede @ 2025-06-20 17:58 UTC (permalink / raw)
  To: Ilpo Järvinen, Andy Shevchenko, Armin Wolf
  Cc: Hans de Goede, platform-driver-x86

dell_wmi_ddv_get_property() gets called with psy->extensions_sem
read-locked, it calls dell_wmi_ddv_battery_translate() which calls
power_supply_get_property() on the same psy which again read-locks
psy->extensions_sem.

Lockdep rightfully complains about this:

 ============================================
 WARNING: possible recursive locking detected
...
 kworker/16:3/1230 is trying to acquire lock:
 ffff8c3143417658 (&psy->extensions_sem){++++}-{4:4},
  at: power_supply_get_property.part.0+0x23/0x160
 but task is already holding lock:
 ffff8c3143417658 (&psy->extensions_sem){++++}-{4:4},
  at: power_supply_get_property.part.0+0x23/0x160
...
  Possible unsafe locking scenario:

        CPU0
        ----
   lock(&psy->extensions_sem);
   lock(&psy->extensions_sem);

  *** DEADLOCK ***
...
 Call Trace:
  <TASK>
  ...
  down_read+0x3e/0x180
  ? power_supply_get_property.part.0+0x23/0x160
  power_supply_get_property.part.0+0x23/0x160
  dell_wmi_ddv_battery_translate+0x68/0x1d0 [dell_wmi_ddv]
  ? lock_acquire+0xd9/0x2c0
  dell_wmi_ddv_get_property+0x25/0x240 [dell_wmi_ddv]
  power_supply_get_property.part.0+0x87/0x160
  power_supply_format_property+0xc4/0x3d0
  add_prop_uevent+0x26/0x90
  power_supply_uevent+0xb9/0xf0

This usually works fine, because read-locking can be done multiple times
but if someone tries to write-lock between the 2 read-lock calls then
the second read-lock will block on the write-lock and the write-lock will
be blocked on the first read-lock leading to a deadlock.

The serial is part of the main psy device, not of an extension. Directly
call psy->desc->get_property() in dell_wmi_ddv_battery_translate() to fix
the double-lock issue.

Note this also influences eppid_show() which is called directly rather
then through power_supply_get_property(). This is ok since the ACPI
battery is fully ready to be used when the battery hook's add_battery
callback is called.

Fixes: 058de163a376 ("platform/x86: dell-ddv: Implement the battery matching algorithm")
Signed-off-by: Hans de Goede <hansg@kernel.org>
---
 drivers/platform/x86/dell/dell-wmi-ddv.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c
index 67f3d7158403..95cc3139f271 100644
--- a/drivers/platform/x86/dell/dell-wmi-ddv.c
+++ b/drivers/platform/x86/dell/dell-wmi-ddv.c
@@ -689,9 +689,11 @@ static int dell_wmi_ddv_battery_translate(struct dell_wmi_ddv_data *data,
 
 	dev_dbg(&data->wdev->dev, "Translation cache miss\n");
 
-	/* Perform a translation between a ACPI battery and a battery index */
-
-	ret = power_supply_get_property(battery, POWER_SUPPLY_PROP_SERIAL_NUMBER, &val);
+	/*
+	 * Perform a translation between a ACPI battery and a battery index. Directly call
+	 * desc->get_property() to avoid locking battery->extensions_sem a second time.
+	 */
+	ret = battery->desc->get_property(battery, POWER_SUPPLY_PROP_SERIAL_NUMBER, &val);
 	if (ret < 0)
 		return ret;
 
-- 
2.49.0


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

* Re: [PATCH] platform/x86: dell-ddv: Fix taking the psy->extensions_sem lock twice
  2025-06-20 17:58 [PATCH] platform/x86: dell-ddv: Fix taking the psy->extensions_sem lock twice Hans de Goede
@ 2025-06-22 18:41 ` Armin Wolf
  2025-06-22 20:59   ` Hans de Goede
  0 siblings, 1 reply; 5+ messages in thread
From: Armin Wolf @ 2025-06-22 18:41 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Andy Shevchenko; +Cc: platform-driver-x86

Am 20.06.25 um 19:58 schrieb Hans de Goede:

> dell_wmi_ddv_get_property() gets called with psy->extensions_sem
> read-locked, it calls dell_wmi_ddv_battery_translate() which calls
> power_supply_get_property() on the same psy which again read-locks
> psy->extensions_sem.
>
> Lockdep rightfully complains about this:
>
>   ============================================
>   WARNING: possible recursive locking detected
> ...
>   kworker/16:3/1230 is trying to acquire lock:
>   ffff8c3143417658 (&psy->extensions_sem){++++}-{4:4},
>    at: power_supply_get_property.part.0+0x23/0x160
>   but task is already holding lock:
>   ffff8c3143417658 (&psy->extensions_sem){++++}-{4:4},
>    at: power_supply_get_property.part.0+0x23/0x160
> ...
>    Possible unsafe locking scenario:
>
>          CPU0
>          ----
>     lock(&psy->extensions_sem);
>     lock(&psy->extensions_sem);
>
>    *** DEADLOCK ***
> ...
>   Call Trace:
>    <TASK>
>    ...
>    down_read+0x3e/0x180
>    ? power_supply_get_property.part.0+0x23/0x160
>    power_supply_get_property.part.0+0x23/0x160
>    dell_wmi_ddv_battery_translate+0x68/0x1d0 [dell_wmi_ddv]
>    ? lock_acquire+0xd9/0x2c0
>    dell_wmi_ddv_get_property+0x25/0x240 [dell_wmi_ddv]
>    power_supply_get_property.part.0+0x87/0x160
>    power_supply_format_property+0xc4/0x3d0
>    add_prop_uevent+0x26/0x90
>    power_supply_uevent+0xb9/0xf0
>
> This usually works fine, because read-locking can be done multiple times
> but if someone tries to write-lock between the 2 read-lock calls then
> the second read-lock will block on the write-lock and the write-lock will
> be blocked on the first read-lock leading to a deadlock.
>
> The serial is part of the main psy device, not of an extension. Directly
> call psy->desc->get_property() in dell_wmi_ddv_battery_translate() to fix
> the double-lock issue.
>
> Note this also influences eppid_show() which is called directly rather
> then through power_supply_get_property(). This is ok since the ACPI
> battery is fully ready to be used when the battery hook's add_battery
> callback is called.

Thank you very much for finding this issue, but i think that simply calling battery->desc->get_property()
is not the right solution for this:

1. We should still call psy_desc_has_property() to determine if the power supply actually support
    POWER_SUPPLY_PROP_SERIAL_NUMBER.

2. At least another power supply extension user (the uniwill-laptop driver currently being under review)
    suffers from a similar problem, so a more generic solution is needed.

Maybe we could introduce a new function for reading power supply properties that ignores any
power supply extensions? This way future extension could use this function too.

I envision something like this:
	
int power_supply_get_property_direct(struct power_supply *psy,
			    enum power_supply_property psp,
			    union power_supply_propval *val)
{
	if (atomic_read(&psy->use_cnt) <= 0) {
		if (!psy->initialized)
			return -EAGAIN;
		return -ENODEV;
	}

	if (psy_desc_has_property(psy->desc, psp))
		return psy->desc->get_property(psy, psp, val);
	else if (power_supply_battery_info_has_prop(psy->battery_info, psp))
		return power_supply_battery_info_get_prop(psy->battery_info, psp, val);
	else
		return -EINVAL;
}
EXPORT_SYMBOL_GPL(power_supply_get_property_direct);

It basically is power_supply_get_property() without the extension logic. I can also write some
documentation on how to implement power supply extensions in general.

What do you thing?

Thanks,
Armin Wolf

> Fixes: 058de163a376 ("platform/x86: dell-ddv: Implement the battery matching algorithm")
> Signed-off-by: Hans de Goede <hansg@kernel.org>
> ---
>   drivers/platform/x86/dell/dell-wmi-ddv.c | 8 +++++---
>   1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c
> index 67f3d7158403..95cc3139f271 100644
> --- a/drivers/platform/x86/dell/dell-wmi-ddv.c
> +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c
> @@ -689,9 +689,11 @@ static int dell_wmi_ddv_battery_translate(struct dell_wmi_ddv_data *data,
>   
>   	dev_dbg(&data->wdev->dev, "Translation cache miss\n");
>   
> -	/* Perform a translation between a ACPI battery and a battery index */
> -
> -	ret = power_supply_get_property(battery, POWER_SUPPLY_PROP_SERIAL_NUMBER, &val);
> +	/*
> +	 * Perform a translation between a ACPI battery and a battery index. Directly call
> +	 * desc->get_property() to avoid locking battery->extensions_sem a second time.
> +	 */
> +	ret = battery->desc->get_property(battery, POWER_SUPPLY_PROP_SERIAL_NUMBER, &val);
>   	if (ret < 0)
>   		return ret;
>   

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

* Re: [PATCH] platform/x86: dell-ddv: Fix taking the psy->extensions_sem lock twice
  2025-06-22 18:41 ` Armin Wolf
@ 2025-06-22 20:59   ` Hans de Goede
  2025-06-22 22:08     ` Armin Wolf
  0 siblings, 1 reply; 5+ messages in thread
From: Hans de Goede @ 2025-06-22 20:59 UTC (permalink / raw)
  To: Armin Wolf, Ilpo Järvinen, Andy Shevchenko,
	Sebastian Reichel
  Cc: platform-driver-x86, Linux PM

+ Cc sre, whom I should have Cc-ed from the beginning.

Hi Armin,

On 22-Jun-25 8:41 PM, Armin Wolf wrote:
> Am 20.06.25 um 19:58 schrieb Hans de Goede:
> 
>> dell_wmi_ddv_get_property() gets called with psy->extensions_sem
>> read-locked, it calls dell_wmi_ddv_battery_translate() which calls
>> power_supply_get_property() on the same psy which again read-locks
>> psy->extensions_sem.
>>
>> Lockdep rightfully complains about this:
>>
>>   ============================================
>>   WARNING: possible recursive locking detected
>> ...
>>   kworker/16:3/1230 is trying to acquire lock:
>>   ffff8c3143417658 (&psy->extensions_sem){++++}-{4:4},
>>    at: power_supply_get_property.part.0+0x23/0x160
>>   but task is already holding lock:
>>   ffff8c3143417658 (&psy->extensions_sem){++++}-{4:4},
>>    at: power_supply_get_property.part.0+0x23/0x160
>> ...
>>    Possible unsafe locking scenario:
>>
>>          CPU0
>>          ----
>>     lock(&psy->extensions_sem);
>>     lock(&psy->extensions_sem);
>>
>>    *** DEADLOCK ***
>> ...
>>   Call Trace:
>>    <TASK>
>>    ...
>>    down_read+0x3e/0x180
>>    ? power_supply_get_property.part.0+0x23/0x160
>>    power_supply_get_property.part.0+0x23/0x160
>>    dell_wmi_ddv_battery_translate+0x68/0x1d0 [dell_wmi_ddv]
>>    ? lock_acquire+0xd9/0x2c0
>>    dell_wmi_ddv_get_property+0x25/0x240 [dell_wmi_ddv]
>>    power_supply_get_property.part.0+0x87/0x160
>>    power_supply_format_property+0xc4/0x3d0
>>    add_prop_uevent+0x26/0x90
>>    power_supply_uevent+0xb9/0xf0
>>
>> This usually works fine, because read-locking can be done multiple times
>> but if someone tries to write-lock between the 2 read-lock calls then
>> the second read-lock will block on the write-lock and the write-lock will
>> be blocked on the first read-lock leading to a deadlock.
>>
>> The serial is part of the main psy device, not of an extension. Directly
>> call psy->desc->get_property() in dell_wmi_ddv_battery_translate() to fix
>> the double-lock issue.
>>
>> Note this also influences eppid_show() which is called directly rather
>> then through power_supply_get_property(). This is ok since the ACPI
>> battery is fully ready to be used when the battery hook's add_battery
>> callback is called.
> 
> Thank you very much for finding this issue, but i think that simply calling battery->desc->get_property()
> is not the right solution for this:
> 
> 1. We should still call psy_desc_has_property() to determine if the power supply actually support
>    POWER_SUPPLY_PROP_SERIAL_NUMBER.

Although it is currently not enforced in power_supply_core.c it seems
reasonable to assume that any powersupply must have a get_property
callback in their desc (the core also unconditionally calls this).

And most (all?) psy drivers I've seen have a default which returns -EINVAL
in their get_property() implementation which works just as well as
calling has_prop ...

My bigger worry is the lack of the:

>     if (atomic_read(&psy->use_cnt) <= 0) {
>         if (!psy->initialized)
>             return -EAGAIN;
>         return -ENODEV;
>     }

Check TBH.

> 2. At least another power supply extension user (the uniwill-laptop driver currently being under review)
>    suffers from a similar problem, so a more generic solution is needed.
> 
> Maybe we could introduce a new function for reading power supply properties that ignores any
> power supply extensions? This way future extension could use this function too.
> 
> I envision something like this:
>     
> int power_supply_get_property_direct(struct power_supply *psy,
>                 enum power_supply_property psp,
>                 union power_supply_propval *val)
> {
>     if (atomic_read(&psy->use_cnt) <= 0) {
>         if (!psy->initialized)
>             return -EAGAIN;
>         return -ENODEV;
>     }
> 
>     if (psy_desc_has_property(psy->desc, psp))
>         return psy->desc->get_property(psy, psp, val);
>     else if (power_supply_battery_info_has_prop(psy->battery_info, psp))
>         return power_supply_battery_info_get_prop(psy->battery_info, psp, val);
>     else
>         return -EINVAL;
> }
> EXPORT_SYMBOL_GPL(power_supply_get_property_direct);
> 
> It basically is power_supply_get_property() without the extension logic.

While working on this fix I was thinking that something like this would be useful,
so +1 for this.

Maybe first do a prep patch where the extension handling in
power_supply_get_property() is moved last, then power_supply_get_property()
can just wrap this new helprr and on -EINVAL check the extensions.

Actually if you move the extensions check to last then the whole doublelock
issue goes away because the serial-number will be found before checking
extensions.

Or if you want to keep checking the extensions first change
the current power_supply_get_property() into a new
__power_supply_get_property() with a "bool check_extensions"
argument and make power_supply_get_property() wrap it pasing true
for check_extensions. Or some such, whatever you do try to avoid code
duplication but you already know this ...

> I can also write some
> documentation on how to implement power supply extensions in general.

That would also be good to have.

Regards,

Hans






>> Fixes: 058de163a376 ("platform/x86: dell-ddv: Implement the battery matching algorithm")
>> Signed-off-by: Hans de Goede <hansg@kernel.org>
>> ---
>>   drivers/platform/x86/dell/dell-wmi-ddv.c | 8 +++++---
>>   1 file changed, 5 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c
>> index 67f3d7158403..95cc3139f271 100644
>> --- a/drivers/platform/x86/dell/dell-wmi-ddv.c
>> +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c
>> @@ -689,9 +689,11 @@ static int dell_wmi_ddv_battery_translate(struct dell_wmi_ddv_data *data,
>>         dev_dbg(&data->wdev->dev, "Translation cache miss\n");
>>   -    /* Perform a translation between a ACPI battery and a battery index */
>> -
>> -    ret = power_supply_get_property(battery, POWER_SUPPLY_PROP_SERIAL_NUMBER, &val);
>> +    /*
>> +     * Perform a translation between a ACPI battery and a battery index. Directly call
>> +     * desc->get_property() to avoid locking battery->extensions_sem a second time.
>> +     */
>> +    ret = battery->desc->get_property(battery, POWER_SUPPLY_PROP_SERIAL_NUMBER, &val);
>>       if (ret < 0)
>>           return ret;
>>   


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

* Re: [PATCH] platform/x86: dell-ddv: Fix taking the psy->extensions_sem lock twice
  2025-06-22 20:59   ` Hans de Goede
@ 2025-06-22 22:08     ` Armin Wolf
  2025-06-23 22:35       ` Sebastian Reichel
  0 siblings, 1 reply; 5+ messages in thread
From: Armin Wolf @ 2025-06-22 22:08 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Andy Shevchenko,
	Sebastian Reichel
  Cc: platform-driver-x86, Linux PM

Am 22.06.25 um 22:59 schrieb Hans de Goede:

> + Cc sre, whom I should have Cc-ed from the beginning.
>
> Hi Armin,
>
> On 22-Jun-25 8:41 PM, Armin Wolf wrote:
>> Am 20.06.25 um 19:58 schrieb Hans de Goede:
>>
>>> dell_wmi_ddv_get_property() gets called with psy->extensions_sem
>>> read-locked, it calls dell_wmi_ddv_battery_translate() which calls
>>> power_supply_get_property() on the same psy which again read-locks
>>> psy->extensions_sem.
>>>
>>> Lockdep rightfully complains about this:
>>>
>>>    ============================================
>>>    WARNING: possible recursive locking detected
>>> ...
>>>    kworker/16:3/1230 is trying to acquire lock:
>>>    ffff8c3143417658 (&psy->extensions_sem){++++}-{4:4},
>>>     at: power_supply_get_property.part.0+0x23/0x160
>>>    but task is already holding lock:
>>>    ffff8c3143417658 (&psy->extensions_sem){++++}-{4:4},
>>>     at: power_supply_get_property.part.0+0x23/0x160
>>> ...
>>>     Possible unsafe locking scenario:
>>>
>>>           CPU0
>>>           ----
>>>      lock(&psy->extensions_sem);
>>>      lock(&psy->extensions_sem);
>>>
>>>     *** DEADLOCK ***
>>> ...
>>>    Call Trace:
>>>     <TASK>
>>>     ...
>>>     down_read+0x3e/0x180
>>>     ? power_supply_get_property.part.0+0x23/0x160
>>>     power_supply_get_property.part.0+0x23/0x160
>>>     dell_wmi_ddv_battery_translate+0x68/0x1d0 [dell_wmi_ddv]
>>>     ? lock_acquire+0xd9/0x2c0
>>>     dell_wmi_ddv_get_property+0x25/0x240 [dell_wmi_ddv]
>>>     power_supply_get_property.part.0+0x87/0x160
>>>     power_supply_format_property+0xc4/0x3d0
>>>     add_prop_uevent+0x26/0x90
>>>     power_supply_uevent+0xb9/0xf0
>>>
>>> This usually works fine, because read-locking can be done multiple times
>>> but if someone tries to write-lock between the 2 read-lock calls then
>>> the second read-lock will block on the write-lock and the write-lock will
>>> be blocked on the first read-lock leading to a deadlock.
>>>
>>> The serial is part of the main psy device, not of an extension. Directly
>>> call psy->desc->get_property() in dell_wmi_ddv_battery_translate() to fix
>>> the double-lock issue.
>>>
>>> Note this also influences eppid_show() which is called directly rather
>>> then through power_supply_get_property(). This is ok since the ACPI
>>> battery is fully ready to be used when the battery hook's add_battery
>>> callback is called.
>> Thank you very much for finding this issue, but i think that simply calling battery->desc->get_property()
>> is not the right solution for this:
>>
>> 1. We should still call psy_desc_has_property() to determine if the power supply actually support
>>     POWER_SUPPLY_PROP_SERIAL_NUMBER.
> Although it is currently not enforced in power_supply_core.c it seems
> reasonable to assume that any powersupply must have a get_property
> callback in their desc (the core also unconditionally calls this).
>
> And most (all?) psy drivers I've seen have a default which returns -EINVAL
> in their get_property() implementation which works just as well as
> calling has_prop ...
>
> My bigger worry is the lack of the:
>
>>      if (atomic_read(&psy->use_cnt) <= 0) {
>>          if (!psy->initialized)
>>              return -EAGAIN;
>>          return -ENODEV;
>>      }
> Check TBH.
>
>> 2. At least another power supply extension user (the uniwill-laptop driver currently being under review)
>>     suffers from a similar problem, so a more generic solution is needed.
>>
>> Maybe we could introduce a new function for reading power supply properties that ignores any
>> power supply extensions? This way future extension could use this function too.
>>
>> I envision something like this:
>>      
>> int power_supply_get_property_direct(struct power_supply *psy,
>>                  enum power_supply_property psp,
>>                  union power_supply_propval *val)
>> {
>>      if (atomic_read(&psy->use_cnt) <= 0) {
>>          if (!psy->initialized)
>>              return -EAGAIN;
>>          return -ENODEV;
>>      }
>>
>>      if (psy_desc_has_property(psy->desc, psp))
>>          return psy->desc->get_property(psy, psp, val);
>>      else if (power_supply_battery_info_has_prop(psy->battery_info, psp))
>>          return power_supply_battery_info_get_prop(psy->battery_info, psp, val);
>>      else
>>          return -EINVAL;
>> }
>> EXPORT_SYMBOL_GPL(power_supply_get_property_direct);
>>
>> It basically is power_supply_get_property() without the extension logic.
> While working on this fix I was thinking that something like this would be useful,
> so +1 for this.
>
> Maybe first do a prep patch where the extension handling in
> power_supply_get_property() is moved last, then power_supply_get_property()
> can just wrap this new helprr and on -EINVAL check the extensions.
>
> Actually if you move the extensions check to last then the whole doublelock
> issue goes away because the serial-number will be found before checking
> extensions.
>
> Or if you want to keep checking the extensions first change
> the current power_supply_get_property() into a new
> __power_supply_get_property() with a "bool check_extensions"
> argument and make power_supply_get_property() wrap it pasing true
> for check_extensions. Or some such, whatever you do try to avoid code
> duplication but you already know this ...
>> I can also write some
>> documentation on how to implement power supply extensions in general.
> That would also be good to have.
>
> Regards,
>
> Hans

Alright, i will send the necessary patches soon.

Thanks,
Armin Wolf

>>> Fixes: 058de163a376 ("platform/x86: dell-ddv: Implement the battery matching algorithm")
>>> Signed-off-by: Hans de Goede <hansg@kernel.org>
>>> ---
>>>    drivers/platform/x86/dell/dell-wmi-ddv.c | 8 +++++---
>>>    1 file changed, 5 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c
>>> index 67f3d7158403..95cc3139f271 100644
>>> --- a/drivers/platform/x86/dell/dell-wmi-ddv.c
>>> +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c
>>> @@ -689,9 +689,11 @@ static int dell_wmi_ddv_battery_translate(struct dell_wmi_ddv_data *data,
>>>          dev_dbg(&data->wdev->dev, "Translation cache miss\n");
>>>    -    /* Perform a translation between a ACPI battery and a battery index */
>>> -
>>> -    ret = power_supply_get_property(battery, POWER_SUPPLY_PROP_SERIAL_NUMBER, &val);
>>> +    /*
>>> +     * Perform a translation between a ACPI battery and a battery index. Directly call
>>> +     * desc->get_property() to avoid locking battery->extensions_sem a second time.
>>> +     */
>>> +    ret = battery->desc->get_property(battery, POWER_SUPPLY_PROP_SERIAL_NUMBER, &val);
>>>        if (ret < 0)
>>>            return ret;
>>>    
>

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

* Re: [PATCH] platform/x86: dell-ddv: Fix taking the psy->extensions_sem lock twice
  2025-06-22 22:08     ` Armin Wolf
@ 2025-06-23 22:35       ` Sebastian Reichel
  0 siblings, 0 replies; 5+ messages in thread
From: Sebastian Reichel @ 2025-06-23 22:35 UTC (permalink / raw)
  To: Armin Wolf
  Cc: Hans de Goede, Ilpo Järvinen, Andy Shevchenko,
	platform-driver-x86, Linux PM, Thomas Weißschuh

[-- Attachment #1: Type: text/plain, Size: 6279 bytes --]

Hi,

On Mon, Jun 23, 2025 at 12:08:04AM +0200, Armin Wolf wrote:
> Am 22.06.25 um 22:59 schrieb Hans de Goede:
> 
> > + Cc sre, whom I should have Cc-ed from the beginning.
> > 
> > Hi Armin,
> > 
> > On 22-Jun-25 8:41 PM, Armin Wolf wrote:
> > > Am 20.06.25 um 19:58 schrieb Hans de Goede:
> > > 
> > > > dell_wmi_ddv_get_property() gets called with psy->extensions_sem
> > > > read-locked, it calls dell_wmi_ddv_battery_translate() which calls
> > > > power_supply_get_property() on the same psy which again read-locks
> > > > psy->extensions_sem.
> > > > 
> > > > Lockdep rightfully complains about this:
> > > > 
> > > >    ============================================
> > > >    WARNING: possible recursive locking detected
> > > > ...
> > > >    kworker/16:3/1230 is trying to acquire lock:
> > > >    ffff8c3143417658 (&psy->extensions_sem){++++}-{4:4},
> > > >     at: power_supply_get_property.part.0+0x23/0x160
> > > >    but task is already holding lock:
> > > >    ffff8c3143417658 (&psy->extensions_sem){++++}-{4:4},
> > > >     at: power_supply_get_property.part.0+0x23/0x160
> > > > ...
> > > >     Possible unsafe locking scenario:
> > > > 
> > > >           CPU0
> > > >           ----
> > > >      lock(&psy->extensions_sem);
> > > >      lock(&psy->extensions_sem);
> > > > 
> > > >     *** DEADLOCK ***
> > > > ...
> > > >    Call Trace:
> > > >     <TASK>
> > > >     ...
> > > >     down_read+0x3e/0x180
> > > >     ? power_supply_get_property.part.0+0x23/0x160
> > > >     power_supply_get_property.part.0+0x23/0x160
> > > >     dell_wmi_ddv_battery_translate+0x68/0x1d0 [dell_wmi_ddv]
> > > >     ? lock_acquire+0xd9/0x2c0
> > > >     dell_wmi_ddv_get_property+0x25/0x240 [dell_wmi_ddv]
> > > >     power_supply_get_property.part.0+0x87/0x160
> > > >     power_supply_format_property+0xc4/0x3d0
> > > >     add_prop_uevent+0x26/0x90
> > > >     power_supply_uevent+0xb9/0xf0
> > > > 
> > > > This usually works fine, because read-locking can be done multiple times
> > > > but if someone tries to write-lock between the 2 read-lock calls then
> > > > the second read-lock will block on the write-lock and the write-lock will
> > > > be blocked on the first read-lock leading to a deadlock.
> > > > 
> > > > The serial is part of the main psy device, not of an extension. Directly
> > > > call psy->desc->get_property() in dell_wmi_ddv_battery_translate() to fix
> > > > the double-lock issue.
> > > > 
> > > > Note this also influences eppid_show() which is called directly rather
> > > > then through power_supply_get_property(). This is ok since the ACPI
> > > > battery is fully ready to be used when the battery hook's add_battery
> > > > callback is called.
> > > Thank you very much for finding this issue, but i think that simply calling battery->desc->get_property()
> > > is not the right solution for this:
> > > 
> > > 1. We should still call psy_desc_has_property() to determine if the power supply actually support
> > >     POWER_SUPPLY_PROP_SERIAL_NUMBER.
> > Although it is currently not enforced in power_supply_core.c it seems
> > reasonable to assume that any powersupply must have a get_property
> > callback in their desc (the core also unconditionally calls this).
> > 
> > And most (all?) psy drivers I've seen have a default which returns -EINVAL
> > in their get_property() implementation which works just as well as
> > calling has_prop ...
> > 
> > My bigger worry is the lack of the:
> > 
> > >      if (atomic_read(&psy->use_cnt) <= 0) {
> > >          if (!psy->initialized)
> > >              return -EAGAIN;
> > >          return -ENODEV;
> > >      }
> > Check TBH.
> > 
> > > 2. At least another power supply extension user (the uniwill-laptop driver currently being under review)
> > >     suffers from a similar problem, so a more generic solution is needed.
> > > 
> > > Maybe we could introduce a new function for reading power supply properties that ignores any
> > > power supply extensions? This way future extension could use this function too.
> > > 
> > > I envision something like this:
> > > int power_supply_get_property_direct(struct power_supply *psy,
> > >                  enum power_supply_property psp,
> > >                  union power_supply_propval *val)
> > > {
> > >      if (atomic_read(&psy->use_cnt) <= 0) {
> > >          if (!psy->initialized)
> > >              return -EAGAIN;
> > >          return -ENODEV;
> > >      }
> > > 
> > >      if (psy_desc_has_property(psy->desc, psp))
> > >          return psy->desc->get_property(psy, psp, val);
> > >      else if (power_supply_battery_info_has_prop(psy->battery_info, psp))
> > >          return power_supply_battery_info_get_prop(psy->battery_info, psp, val);
> > >      else
> > >          return -EINVAL;
> > > }
> > > EXPORT_SYMBOL_GPL(power_supply_get_property_direct);
> > > 
> > > It basically is power_supply_get_property() without the extension logic.
> > While working on this fix I was thinking that something like this would be useful,
> > so +1 for this.
> > 
> > Maybe first do a prep patch where the extension handling in
> > power_supply_get_property() is moved last, then power_supply_get_property()
> > can just wrap this new helprr and on -EINVAL check the extensions.
> > 
> > Actually if you move the extensions check to last then the whole doublelock
> > issue goes away because the serial-number will be found before checking
> > extensions.
> > 
> > Or if you want to keep checking the extensions first change
> > the current power_supply_get_property() into a new
> > __power_supply_get_property() with a "bool check_extensions"
> > argument and make power_supply_get_property() wrap it pasing true
> > for check_extensions. Or some such, whatever you do try to avoid code
> > duplication but you already know this ...
> > > I can also write some
> > > documentation on how to implement power supply extensions in general.
> > That would also be good to have.
> > 
> > Regards,
> > 
> > Hans
> 
> Alright, i will send the necessary patches soon.

Sounds good to me. Please also Cc Thomas Weißschuh, who implemented
all the extension code :)

Greetings,

-- Sebastian

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

end of thread, other threads:[~2025-06-23 22:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-20 17:58 [PATCH] platform/x86: dell-ddv: Fix taking the psy->extensions_sem lock twice Hans de Goede
2025-06-22 18:41 ` Armin Wolf
2025-06-22 20:59   ` Hans de Goede
2025-06-22 22:08     ` Armin Wolf
2025-06-23 22:35       ` Sebastian Reichel

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