Linux LED subsystem development
 help / color / mirror / Atom feed
* "leds: trigger: use RCU to protect the led_cdevs list" triggers RCU error checks
@ 2023-04-11 18:15 Hans de Goede
  2023-04-11 19:38 ` Johannes Berg
  0 siblings, 1 reply; 5+ messages in thread
From: Hans de Goede @ 2023-04-11 18:15 UTC (permalink / raw)
  To: Johannes Berg, Pavel Machek, Lee Jones; +Cc: Linux LED Subsystem

Hi Johannes, et al., 

Sorry to bring the bearer of bad news, but your commit 2a5a8fa8b23
("leds: trigger: use RCU to protect the led_cdevs list"),
is causing the following RCU warning when used with blinking
triggers on I2C LED controllers which support hw blinking.

The specific problem is drivers/leds/led-triggers.c:
led_trigger_blink_setup() which does:

        rcu_read_lock();
        list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) {
                if (oneshot)
                        led_blink_set_oneshot(led_cdev, delay_on, delay_off,
                                              invert);
                else
                        led_blink_set(led_cdev, delay_on, delay_off);
        }
        rcu_read_unlock();

And that led_blink_set() call then hits this path:

        if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
            led_cdev->blink_set &&
            !led_cdev->blink_set(led_cdev, delay_on, delay_off))
                return;

Which calls directly into the LED controller driver which
talks to the LED controller over I2C which may sleep.

AFAICT (not much RCU experience) the problem basically is that in
a rcu_read_lock() section the code may not sleep. While
led_blink_set() can sleep. For completeness sake I've added
the full backtrace at the end of this email, but it really is not
that interesting.

From the original commit I understand that RCU was chosen
because of other hairy issues. For non blinking triggers this
works fine, because led_set_brightness() is guaranteed to
not sleep using a workqueue to delay the set brightness
in case it would sleep.

We could do something similar for led_set_blink, at least
when called from led_trigger_blink[_oneshot], to avoid
this issue.

But I wonder if someone has any better ideas ?

Regards,

Hans











[  832.605062] ------------[ cut here ]------------
[  832.605085] Voluntary context switch within RCU read-side critical section!
[  832.605119] WARNING: CPU: 2 PID: 370 at kernel/rcu/tree_plugin.h:318 rcu_note_context_switch+0x4ee/0x690
[  832.605175] Modules linked in: snd_seq_dummy snd_hrtimer joydev wacom bq25890_charger(E) bq27xxx_battery_i2c(E) i2c_hid_of(E) goodix_ts hideep lp855x_bl bq27xxx_battery(E) snd_soc_sst_byt_cht_nocodec qrtr brcmfmac_wcc mei_pxp dwc3 mei_hdcp intel_powerclamp coretemp kvm_intel intel_rapl_msr gpio_keys x86_android_tablets(E) udc_core ulpi brcmfmac kvm brcmutil irqbypass i2c_cht_wc intel_cstate snd_sof_acpi_intel_byt cfg80211 snd_sof_acpi snd_sof_intel_atom leds_cht_wcove(E) snd_sof_xtensa_dsp snd_sof pcspkr extcon_intel_cht_wc snd_sof_utils lpc_ich snd_intel_sst_acpi intel_xhci_usb_role_switch snd_soc_acpi_intel_match snd_intel_sst_core snd_soc_sst_atom_hifi2_platform hid_sensor_magn_3d snd_soc_acpi snd_intel_dspcfg snd_intel_sdw_acpi hid_sensor_gyro_3d hid_sensor_accel_3d hid_sensor_incl_3d hid_sensor_rotation hid_sensor_als hid_sensor_trigger hid_sensor_iio_common industrialio_triggered_buffer kfifo_buf industrialio snd_soc_core snd_compress ac97_bus hci_uart(E) btqca(E) btrtl(E) btbcm(E) snd_pcm_dmaengine btintel(E)
[  832.605676]  snd_hdmi_lpe_audio intel_hid sparse_keymap intel_soc_pmic_bxtwc snd_seq bluetooth snd_seq_device ov5693 snd_pcm atomisp_ov2722(C) v4l2_fwnode v4l2_async atomisp_gmin_platform(C) videodev spi_pxa2xx_platform mc ecdh_generic snd_timer rfkill_gpio processor_thermal_device_pci_legacy snd rfkill dw_dmac processor_thermal_device soundcore processor_thermal_rfim processor_thermal_mbox mei_txe processor_thermal_rapl vfat fat int3400_thermal intel_rapl_common int3403_thermal mei soc_button_array int3406_thermal acpi_thermal_rel int340x_thermal_zone dptf_power(E) intel_soc_dts_iosf intel_int0002_vgpio dwc3_pci acpi_pad(E) zram hid_sensor_hub intel_ishtp_hid i915(E) crct10dif_pclmul mmc_block crc32_pclmul crc32c_intel i2c_algo_bit drm_buddy(E) drm_display_helper(E) ghash_clmulni_intel sha512_ssse3 wdat_wdt intel_ish_ipc cec video(E) intel_ishtp sdhci_acpi ttm(E) sdhci wmi i2c_hid_acpi(E) i2c_hid(E) mmc_core pwm_lpss_platform pwm_lpss ip6_tables ip_tables i2c_dev fuse
[  832.606213] CPU: 2 PID: 370 Comm: kworker/2:4 Tainted: G        WC  E      6.3.0-rc4+ #179
[  832.606237] Hardware name: Intel Corporation CHERRYVIEW D1 PLATFORM/Cherry Trail Tablet, BIOS CHTTYETI.X64.0514.R1B.1701240934 01/24/2017
[  832.606251] Workqueue: events power_supply_changed_work
[  832.606281] RIP: 0010:rcu_note_context_switch+0x4ee/0x690
[  832.606311] Code: 49 89 3f 49 83 bc 24 98 00 00 00 00 0f 85 66 fe ff ff e9 58 fe ff ff 48 c7 c7 48 e4 75 83 c6 05 b9 0c b2 01 01 e8 92 4b f5 ff <0f> 0b e9 70 fb ff ff a9 ff ff ff 7f 0f 84 2c fc ff ff 65 48 8b 3c
[  832.606328] RSP: 0018:ffffbd0480737908 EFLAGS: 00010092
[  832.606348] RAX: 000000000000003f RBX: ffff9ff8fa933900 RCX: 0000000000000000
[  832.606362] RDX: 0000000000000003 RSI: ffffffff837b6da0 RDI: 00000000ffffffff
[  832.606374] RBP: 0000000000000000 R08: 0000000000000000 R09: ffffbd04807377c0
[  832.606386] R10: 0000000000000003 R11: ffffffff83b44168 R12: ffff9ff8fa932a80
[  832.606398] R13: ffff9ff8867a0000 R14: ffffbd0480737a78 R15: ffff9ff8810e5060
[  832.606411] FS:  0000000000000000(0000) GS:ffff9ff8fa900000(0000) knlGS:0000000000000000
[  832.606427] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  832.606439] CR2: 00007f1e96b510c8 CR3: 0000000155a20000 CR4: 00000000001006e0
[  832.606453] Call Trace:
[  832.606466]  <TASK>
[  832.606487]  __schedule+0x9f/0x1480
[  832.606527]  schedule+0x5d/0xe0
[  832.606549]  schedule_timeout+0x79/0x140
[  832.606572]  ? __pfx_process_timeout+0x10/0x10
[  832.606599]  wait_for_completion_timeout+0x6f/0x140
[  832.606627]  i2c_dw_xfer+0x101/0x460
[  832.606659]  ? psi_group_change+0x168/0x400
[  832.606680]  __i2c_transfer+0x172/0x6d0
[  832.606709]  i2c_smbus_xfer_emulated+0x27d/0x9c0
[  832.606732]  ? __schedule+0x430/0x1480
[  832.606753]  ? preempt_count_add+0x6a/0xa0
[  832.606778]  ? get_nohz_timer_target+0x18/0x190
[  832.606796]  ? lock_timer_base+0x61/0x80
[  832.606817]  ? preempt_count_add+0x6a/0xa0
[  832.606842]  __i2c_smbus_xfer+0xa2/0x3f0
[  832.606862]  i2c_smbus_xfer+0x66/0xf0
[  832.606882]  i2c_smbus_read_byte_data+0x41/0x70
[  832.606901]  ? _raw_spin_unlock_irqrestore+0x23/0x40
[  832.606922]  ? __pm_runtime_suspend+0x46/0xc0
[  832.606946]  cht_wc_byte_reg_read+0x2e/0x60
[  832.606972]  _regmap_read+0x5c/0x120
[  832.606997]  _regmap_update_bits+0x96/0xc0
[  832.607023]  regmap_update_bits_base+0x5b/0x90
[  832.607053]  cht_wc_leds_brightness_get+0x412/0x910 [leds_cht_wcove]
[  832.607094]  led_blink_setup+0x28/0x100
[  832.607119]  led_trigger_blink+0x40/0x70
[  832.607145]  power_supply_update_leds+0x1b7/0x1c0
[  832.607174]  power_supply_changed_work+0x67/0xe0
[  832.607198]  process_one_work+0x1c8/0x3c0
[  832.607222]  worker_thread+0x4d/0x380
[  832.607243]  ? __pfx_worker_thread+0x10/0x10
[  832.607258]  kthread+0xe9/0x110
[  832.607279]  ? __pfx_kthread+0x10/0x10
[  832.607300]  ret_from_fork+0x2c/0x50
[  832.607337]  </TASK>
[  832.607344] ---[ end trace 0000000000000000 ]---



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

* Re: "leds: trigger: use RCU to protect the led_cdevs list" triggers RCU error checks
  2023-04-11 18:15 "leds: trigger: use RCU to protect the led_cdevs list" triggers RCU error checks Hans de Goede
@ 2023-04-11 19:38 ` Johannes Berg
  2023-04-11 21:26   ` Hans de Goede
  0 siblings, 1 reply; 5+ messages in thread
From: Johannes Berg @ 2023-04-11 19:38 UTC (permalink / raw)
  To: Hans de Goede, Pavel Machek, Lee Jones; +Cc: Linux LED Subsystem

Hi,

> Sorry to bring the bearer of bad news, but your commit 2a5a8fa8b23
> ("leds: trigger: use RCU to protect the led_cdevs list"),
> is causing the following RCU warning when used with blinking
> triggers on I2C LED controllers which support hw blinking.

Err, well, surely that is a pre-existing driver bug then?

> The specific problem is drivers/leds/led-triggers.c:
> led_trigger_blink_setup() which does:
> 
>         rcu_read_lock();
>         list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) {
>                 if (oneshot)
>                         led_blink_set_oneshot(led_cdev, delay_on, delay_off,
>                                               invert);
>                 else
>                         led_blink_set(led_cdev, delay_on, delay_off);
>         }
>         rcu_read_unlock();
> 
> And that led_blink_set() call then hits this path:
> 
>         if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
>             led_cdev->blink_set &&
>             !led_cdev->blink_set(led_cdev, delay_on, delay_off))
>                 return;
> 
> Which calls directly into the LED controller driver

Sure, so far so good.

> which talks to the LED controller over I2C which may sleep.

Which seems to me was already wrong before my patch, since the code was:

       read_lock_irqsave(&trig->leddev_list_lock, flags);
       list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
               if (oneshot)
                       led_blink_set_oneshot(led_cdev, delay_on, delay_off,
                                             invert);
               else
                       led_blink_set(led_cdev, delay_on, delay_off);
       }
       read_unlock_irqrestore(&trig->leddev_list_lock, flags);


Surely, the code wasn't allowed to sleep in an _irqsave() section? You'd
just see a different check complain, rather than about RCU, I guess.


So maybe you're bringing bad news, but I don't think it's for me ;-) I
don't see cht_wc_leds_brightness_get() or a driver/module called
leds_cht_wcove even in linux-next, so I guess you should look wherever
you got _that_ from.

johannes

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

* Re: "leds: trigger: use RCU to protect the led_cdevs list" triggers RCU error checks
  2023-04-11 19:38 ` Johannes Berg
@ 2023-04-11 21:26   ` Hans de Goede
  2023-04-12  8:13     ` Johannes Berg
  0 siblings, 1 reply; 5+ messages in thread
From: Hans de Goede @ 2023-04-11 21:26 UTC (permalink / raw)
  To: Johannes Berg, Pavel Machek, Lee Jones; +Cc: Linux LED Subsystem

Hi Johannes,

Thank you for the quick reply.

On 4/11/23 21:38, Johannes Berg wrote:
> Hi,
> 
>> Sorry to bring the bearer of bad news, but your commit 2a5a8fa8b23
>> ("leds: trigger: use RCU to protect the led_cdevs list"),
>> is causing the following RCU warning when used with blinking
>> triggers on I2C LED controllers which support hw blinking.
> 
> Err, well, surely that is a pre-existing driver bug then?

So I just checked and the following LED drivers all have
a blink_set() implementation which calls mutex_lock()
and/or does I2C transfers:

leds-an30259a.c
leds-aw2013.c
leds-bd2802.c
leds-lp3944.c
leds-pca9532.c
leds-pca963x.c
leds-wm831x-status.c

And I only found one i2c LED controller driver which defers
I2C transfers to a workqueue itself (leds-tca6507.c).

And looking at: include/linux/leds.h

Then the brightness_set callback is explicitly marked as
"Must not sleep." (and there is a brightness_set_blocking
which e.g. I2C drivers can use instead).

So I believe that the intention has always been that
a driver's blink_set callback is allowed to sleep.

With that said you seem to be right that there seems to
be a long standing bug where led_trigger_blink[_oneshot]
calls led_classdev.blink_set() in a context where it may
not sleep.

But that is more of a LED (trigger) core bug then an
issue with the driver(s).

>> The specific problem is drivers/leds/led-triggers.c:
>> led_trigger_blink_setup() which does:
>>
>>         rcu_read_lock();
>>         list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) {
>>                 if (oneshot)
>>                         led_blink_set_oneshot(led_cdev, delay_on, delay_off,
>>                                               invert);
>>                 else
>>                         led_blink_set(led_cdev, delay_on, delay_off);
>>         }
>>         rcu_read_unlock();
>>
>> And that led_blink_set() call then hits this path:
>>
>>         if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
>>             led_cdev->blink_set &&
>>             !led_cdev->blink_set(led_cdev, delay_on, delay_off))
>>                 return;
>>
>> Which calls directly into the LED controller driver
> 
> Sure, so far so good.
> 
>> which talks to the LED controller over I2C which may sleep.
> 
> Which seems to me was already wrong before my patch, since the code was:
> 
>        read_lock_irqsave(&trig->leddev_list_lock, flags);
>        list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
>                if (oneshot)
>                        led_blink_set_oneshot(led_cdev, delay_on, delay_off,
>                                              invert);
>                else
>                        led_blink_set(led_cdev, delay_on, delay_off);
>        }
>        read_unlock_irqrestore(&trig->leddev_list_lock, flags);
> 
> 
> Surely, the code wasn't allowed to sleep in an _irqsave() section? You'd
> just see a different check complain, rather than about RCU, I guess.

Hmm, so the irqsave part of this was introduced by commit 27af8e2c90fb
("leds: trigger: fix potential deadlock with libata") but even before
then I think sleeping here was not allowed, given that an rwlock is
a spinlock variant the non irqsave version also leads to a section
where sleeping is not allowed I believe.

Further git archeology seems to indicate that this problem has existed
for a long long time already. I guess I'm the first user of a trigger
which calls led_trigger_blink[_oneshot] on a led_classdev with
a hw blink_set() implementation which sleeps. Or at least I'm
the first user to do so with various lock-debugging options
enabled ...

> So maybe you're bringing bad news, but I don't think it's for me ;-)

Yes it seems I have unearthed a bug which is much older then your
RCU changes.

> I don't see cht_wc_leds_brightness_get() or a driver/module called
> leds_cht_wcove even in linux-next, so I guess you should look wherever
> you got _that_ from.

Right that is part of a new LED driver I'm working on. I guess I could
fix things in the driver, but this seems to be a (trigger) core bug
so lets try to solve it there.

Regards,

Hans



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

* Re: "leds: trigger: use RCU to protect the led_cdevs list" triggers RCU error checks
  2023-04-11 21:26   ` Hans de Goede
@ 2023-04-12  8:13     ` Johannes Berg
  2023-04-12 21:06       ` Hans de Goede
  0 siblings, 1 reply; 5+ messages in thread
From: Johannes Berg @ 2023-04-12  8:13 UTC (permalink / raw)
  To: Hans de Goede, Pavel Machek, Lee Jones; +Cc: Linux LED Subsystem

Hi,

> So I just checked and the following LED drivers all have
> a blink_set() implementation which calls mutex_lock()
> and/or does I2C transfers:

<snip>

Yay ...

> And looking at: include/linux/leds.h
> 
> Then the brightness_set callback is explicitly marked as
> "Must not sleep." (and there is a brightness_set_blocking
> which e.g. I2C drivers can use instead).
> 
> So I believe that the intention has always been that
> a driver's blink_set callback is allowed to sleep.

I guess I wouldn't really go that far from the lack of a comment saying
it cannot sleep :-)


> With that said you seem to be right that there seems to
> be a long standing bug where led_trigger_blink[_oneshot]
> calls led_classdev.blink_set() in a context where it may
> not sleep.
> 
> But that is more of a LED (trigger) core bug then an
> issue with the driver(s).

IMHO that's arguable, but I'm not going to quibble over it.

> Hmm, so the irqsave part of this was introduced by commit 27af8e2c90fb
> ("leds: trigger: fix potential deadlock with libata") 

Indeed.

> but even before
> then I think sleeping here was not allowed, given that an rwlock is
> a spinlock variant the non irqsave version also leads to a section
> where sleeping is not allowed I believe.

Right. Which goes back to 0b9536c95709 ("leds: Add ability to blink via
simple trigger") where blinking was made possible from a trigger ...

> Further git archeology seems to indicate that this problem has existed
> for a long long time already. I guess I'm the first user of a trigger
> which calls led_trigger_blink[_oneshot] on a led_classdev with
> a hw blink_set() implementation which sleeps. Or at least I'm
> the first user to do so with various lock-debugging options
> enabled ...

Right...

> but this seems to be a (trigger) core bug
> so lets try to solve it there.
> 

It's not so easy to fix I guess, other than maybe to defer to a
workqueue, but then you have the issue of cancelling?

Note that led_trigger_blink() also doesn't document when it's allowed to
be called, and at least in mac80211 (my code, yay) we're calling it from
a timer, so can't possibly sleep there. There's only one other caller in
the power supply code, but that can actually sleep.

I'd have a least thought of srcu if that timer weren't the case in
mac80211, but as is that doesn't help ...

So not sure. Clearly it's a long-standing issue, and given that many
drivers are affected probably better to fix it in the LED core, but I
don't really know my way around it very well either.

johannes

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

* Re: "leds: trigger: use RCU to protect the led_cdevs list" triggers RCU error checks
  2023-04-12  8:13     ` Johannes Berg
@ 2023-04-12 21:06       ` Hans de Goede
  0 siblings, 0 replies; 5+ messages in thread
From: Hans de Goede @ 2023-04-12 21:06 UTC (permalink / raw)
  To: Johannes Berg, Pavel Machek, Lee Jones; +Cc: Linux LED Subsystem

Hi Johannes,

On 4/12/23 10:13, Johannes Berg wrote:
> Hi,
> 
>> So I just checked and the following LED drivers all have
>> a blink_set() implementation which calls mutex_lock()
>> and/or does I2C transfers:
> 
> <snip>
> 
> Yay ...

<snip>

> So not sure. Clearly it's a long-standing issue, and given that many
> drivers are affected probably better to fix it in the LED core, but I
> don't really know my way around it very well either.

I think I've come up with a solution for this.

I'll Cc you on the patch-set for this when it is ready.

Regards,

Hans


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

end of thread, other threads:[~2023-04-12 21:06 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-04-11 18:15 "leds: trigger: use RCU to protect the led_cdevs list" triggers RCU error checks Hans de Goede
2023-04-11 19:38 ` Johannes Berg
2023-04-11 21:26   ` Hans de Goede
2023-04-12  8:13     ` Johannes Berg
2023-04-12 21:06       ` Hans de Goede

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