linux-brain/drivers/leds
Andrea Righi 6aceac2450 leds: trigger: fix potential deadlock with libata
commit 27af8e2c90fba242460b01fa020e6e19ed68c495 upstream.

We have the following potential deadlock condition:

 ========================================================
 WARNING: possible irq lock inversion dependency detected
 5.10.0-rc2+ #25 Not tainted
 --------------------------------------------------------
 swapper/3/0 just changed the state of lock:
 ffff8880063bd618 (&host->lock){-...}-{2:2}, at: ata_bmdma_interrupt+0x27/0x200
 but this lock took another, HARDIRQ-READ-unsafe lock in the past:
  (&trig->leddev_list_lock){.+.?}-{2:2}

 and interrupts could create inverse lock ordering between them.

 other info that might help us debug this:
  Possible interrupt unsafe locking scenario:

        CPU0                    CPU1
        ----                    ----
   lock(&trig->leddev_list_lock);
                                local_irq_disable();
                                lock(&host->lock);
                                lock(&trig->leddev_list_lock);
   <Interrupt>
     lock(&host->lock);

  *** DEADLOCK ***

 no locks held by swapper/3/0.

 the shortest dependencies between 2nd lock and 1st lock:
  -> (&trig->leddev_list_lock){.+.?}-{2:2} ops: 46 {
     HARDIRQ-ON-R at:
                       lock_acquire+0x15f/0x420
                       _raw_read_lock+0x42/0x90
                       led_trigger_event+0x2b/0x70
                       rfkill_global_led_trigger_worker+0x94/0xb0
                       process_one_work+0x240/0x560
                       worker_thread+0x58/0x3d0
                       kthread+0x151/0x170
                       ret_from_fork+0x1f/0x30
     IN-SOFTIRQ-R at:
                       lock_acquire+0x15f/0x420
                       _raw_read_lock+0x42/0x90
                       led_trigger_event+0x2b/0x70
                       kbd_bh+0x9e/0xc0
                       tasklet_action_common.constprop.0+0xe9/0x100
                       tasklet_action+0x22/0x30
                       __do_softirq+0xcc/0x46d
                       run_ksoftirqd+0x3f/0x70
                       smpboot_thread_fn+0x116/0x1f0
                       kthread+0x151/0x170
                       ret_from_fork+0x1f/0x30
     SOFTIRQ-ON-R at:
                       lock_acquire+0x15f/0x420
                       _raw_read_lock+0x42/0x90
                       led_trigger_event+0x2b/0x70
                       rfkill_global_led_trigger_worker+0x94/0xb0
                       process_one_work+0x240/0x560
                       worker_thread+0x58/0x3d0
                       kthread+0x151/0x170
                       ret_from_fork+0x1f/0x30
     INITIAL READ USE at:
                           lock_acquire+0x15f/0x420
                           _raw_read_lock+0x42/0x90
                           led_trigger_event+0x2b/0x70
                           rfkill_global_led_trigger_worker+0x94/0xb0
                           process_one_work+0x240/0x560
                           worker_thread+0x58/0x3d0
                           kthread+0x151/0x170
                           ret_from_fork+0x1f/0x30
   }
   ... key      at: [<ffffffff83da4c00>] __key.0+0x0/0x10
   ... acquired at:
    _raw_read_lock+0x42/0x90
    led_trigger_blink_oneshot+0x3b/0x90
    ledtrig_disk_activity+0x3c/0xa0
    ata_qc_complete+0x26/0x450
    ata_do_link_abort+0xa3/0xe0
    ata_port_freeze+0x2e/0x40
    ata_hsm_qc_complete+0x94/0xa0
    ata_sff_hsm_move+0x177/0x7a0
    ata_sff_pio_task+0xc7/0x1b0
    process_one_work+0x240/0x560
    worker_thread+0x58/0x3d0
    kthread+0x151/0x170
    ret_from_fork+0x1f/0x30

 -> (&host->lock){-...}-{2:2} ops: 69 {
    IN-HARDIRQ-W at:
                     lock_acquire+0x15f/0x420
                     _raw_spin_lock_irqsave+0x52/0xa0
                     ata_bmdma_interrupt+0x27/0x200
                     __handle_irq_event_percpu+0xd5/0x2b0
                     handle_irq_event+0x57/0xb0
                     handle_edge_irq+0x8c/0x230
                     asm_call_irq_on_stack+0xf/0x20
                     common_interrupt+0x100/0x1c0
                     asm_common_interrupt+0x1e/0x40
                     native_safe_halt+0xe/0x10
                     arch_cpu_idle+0x15/0x20
                     default_idle_call+0x59/0x1c0
                     do_idle+0x22c/0x2c0
                     cpu_startup_entry+0x20/0x30
                     start_secondary+0x11d/0x150
                     secondary_startup_64_no_verify+0xa6/0xab
    INITIAL USE at:
                    lock_acquire+0x15f/0x420
                    _raw_spin_lock_irqsave+0x52/0xa0
                    ata_dev_init+0x54/0xe0
                    ata_link_init+0x8b/0xd0
                    ata_port_alloc+0x1f1/0x210
                    ata_host_alloc+0xf1/0x130
                    ata_host_alloc_pinfo+0x14/0xb0
                    ata_pci_sff_prepare_host+0x41/0xa0
                    ata_pci_bmdma_prepare_host+0x14/0x30
                    piix_init_one+0x21f/0x600
                    local_pci_probe+0x48/0x80
                    pci_device_probe+0x105/0x1c0
                    really_probe+0x221/0x490
                    driver_probe_device+0xe9/0x160
                    device_driver_attach+0xb2/0xc0
                    __driver_attach+0x91/0x150
                    bus_for_each_dev+0x81/0xc0
                    driver_attach+0x1e/0x20
                    bus_add_driver+0x138/0x1f0
                    driver_register+0x91/0xf0
                    __pci_register_driver+0x73/0x80
                    piix_init+0x1e/0x2e
                    do_one_initcall+0x5f/0x2d0
                    kernel_init_freeable+0x26f/0x2cf
                    kernel_init+0xe/0x113
                    ret_from_fork+0x1f/0x30
  }
  ... key      at: [<ffffffff83d9fdc0>] __key.6+0x0/0x10
  ... acquired at:
    __lock_acquire+0x9da/0x2370
    lock_acquire+0x15f/0x420
    _raw_spin_lock_irqsave+0x52/0xa0
    ata_bmdma_interrupt+0x27/0x200
    __handle_irq_event_percpu+0xd5/0x2b0
    handle_irq_event+0x57/0xb0
    handle_edge_irq+0x8c/0x230
    asm_call_irq_on_stack+0xf/0x20
    common_interrupt+0x100/0x1c0
    asm_common_interrupt+0x1e/0x40
    native_safe_halt+0xe/0x10
    arch_cpu_idle+0x15/0x20
    default_idle_call+0x59/0x1c0
    do_idle+0x22c/0x2c0
    cpu_startup_entry+0x20/0x30
    start_secondary+0x11d/0x150
    secondary_startup_64_no_verify+0xa6/0xab

This lockdep splat is reported after:
commit e918188611f0 ("locking: More accurate annotations for read_lock()")

To clarify:
 - read-locks are recursive only in interrupt context (when
   in_interrupt() returns true)
 - after acquiring host->lock in CPU1, another cpu (i.e. CPU2) may call
   write_lock(&trig->leddev_list_lock) that would be blocked by CPU0
   that holds trig->leddev_list_lock in read-mode
 - when CPU1 (ata_ac_complete()) tries to read-lock
   trig->leddev_list_lock, it would be blocked by the write-lock waiter
   on CPU2 (because we are not in interrupt context, so the read-lock is
   not recursive)
 - at this point if an interrupt happens on CPU0 and
   ata_bmdma_interrupt() is executed it will try to acquire host->lock,
   that is held by CPU1, that is currently blocked by CPU2, so:

   * CPU0 blocked by CPU1
   * CPU1 blocked by CPU2
   * CPU2 blocked by CPU0

     *** DEADLOCK ***

The deadlock scenario is better represented by the following schema
(thanks to Boqun Feng <boqun.feng@gmail.com> for the schema and the
detailed explanation of the deadlock condition):

 CPU 0:                          CPU 1:                        CPU 2:
 -----                           -----                         -----
 led_trigger_event():
   read_lock(&trig->leddev_list_lock);
 				<workqueue>
 				ata_hsm_qc_complete():
 				  spin_lock_irqsave(&host->lock);
 								write_lock(&trig->leddev_list_lock);
 				  ata_port_freeze():
 				    ata_do_link_abort():
 				      ata_qc_complete():
 					ledtrig_disk_activity():
 					  led_trigger_blink_oneshot():
 					    read_lock(&trig->leddev_list_lock);
 					    // ^ not in in_interrupt() context, so could get blocked by CPU 2
 <interrupt>
   ata_bmdma_interrupt():
     spin_lock_irqsave(&host->lock);

Fix by using read_lock_irqsave/irqrestore() in led_trigger_event(), so
that no interrupt can happen in between, preventing the deadlock
condition.

Apply the same change to led_trigger_blink_setup() as well, since the
same deadlock scenario can also happen in power_supply_update_bat_leds()
-> led_trigger_blink() -> led_trigger_blink_setup() (workqueue context),
and potentially prevent other similar usages.

Link: https://lore.kernel.org/lkml/20201101092614.GB3989@xps-13-7390/
Fixes: eb25cb9956 ("leds: convert IDE trigger to common disk trigger")
Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
Signed-off-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-02-03 23:25:58 +01:00
..
trigger leds: trigger: netdev: fix handling on interface rename 2020-01-04 19:17:05 +01:00
Kconfig leds: netxbig: remove legacy board-file support 2019-07-29 21:04:53 +02:00
led-class-flash.c leds: class: Improve LED and LED flash class registration API 2019-07-25 20:07:50 +02:00
led-class.c leds: core: Flush scheduled work for system suspend 2020-08-19 08:16:11 +02:00
led-core.c leds: Switch to use fwnode instead of be stuck with OF one 2019-08-23 23:39:37 +02:00
led-triggers.c leds: trigger: fix potential deadlock with libata 2021-02-03 23:25:58 +01:00
leds-88pm860x.c leds: 88pm860x: fix use-after-free on unbind 2020-08-11 15:33:36 +02:00
leds-aat1290.c leds: aat1290: Use generic support for composing LED names 2019-07-25 20:07:59 +02:00
leds-adp5520.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 149 2019-05-30 11:25:18 -07:00
leds-an30259a.c leds: an30259a: add a check for devm_regmap_init_i2c 2020-01-04 19:17:03 +01:00
leds-apu.c leds: apu: add pr_fmt prefix for better log output 2019-07-22 21:57:35 +02:00
leds-as3645a.c leds: as3645a: Use generic support for composing LED names 2019-07-25 20:08:00 +02:00
leds-asic3.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-bcm6328.c leds: bcm6328, bcm6358: use devres LED registering function 2020-11-05 11:43:24 +01:00
leds-bcm6358.c leds: bcm6328, bcm6358: use devres LED registering function 2020-11-05 11:43:24 +01:00
leds-bd2802.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-blinkm.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 61 2019-05-24 17:36:45 +02:00
leds-clevo-mail.c treewide: Add SPDX license identifier for more missed files 2019-05-21 10:50:45 +02:00
leds-cobalt-qube.c treewide: Add SPDX license identifier for more missed files 2019-05-21 10:50:45 +02:00
leds-cobalt-raq.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 1 2019-05-21 11:28:39 +02:00
leds-cpcap.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 89 2019-05-24 17:37:52 +02:00
leds-cr0014114.c leds: cr0014114: Use generic support for composing LED names 2019-07-25 20:07:58 +02:00
leds-da903x.c leds: da903x: fix use-after-free on unbind 2020-08-11 15:33:36 +02:00
leds-da9052.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 152 2019-05-30 11:26:32 -07:00
leds-dac124s085.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 330 2019-06-05 17:37:06 +02:00
leds-fsg.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-gpio-register.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-gpio.c leds: gpio: Fix uninitialized gpio label for fwnode based probe 2020-01-29 16:45:29 +01:00
leds-hp6xx.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-ipaq-micro.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-is31fl32xx.c leds: is31fl32xx: Use struct_size() helper 2019-09-01 13:33:29 +02:00
leds-is31fl319x.c leds: is31fl319x: simplify getting the adapter of a client 2019-07-23 20:47:45 +02:00
leds-ktd2692.c leds: ktd2692: Fix a typo in the name of a constant 2019-07-22 21:43:12 +02:00
leds-lm355x.c leds: lm355x: avoid enum conversion warning 2020-08-19 08:16:07 +02:00
leds-lm3530.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 197 2019-05-30 11:29:22 -07:00
leds-lm3532.c leds: lm3532: Fix optional led-max-microamp prop error handling 2019-09-12 20:45:52 +02:00
leds-lm3533.c leds: lm3533: fix use-after-free on unbind 2020-08-11 15:33:36 +02:00
leds-lm3601x.c leds: lm3601x: Use generic support for composing LED names 2019-07-25 20:07:57 +02:00
leds-lm3642.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-lm3692x.c leds: lm3692x: Handle failure to probe the regulator 2020-01-04 19:17:02 +01:00
leds-lm3697.c leds: lm3697: Switch to use fwnode_property_count_uXX() 2019-07-25 20:08:03 +02:00
leds-lm36274.c leds: lm36274: fix use-after-free on unbind 2020-08-11 15:33:36 +02:00
leds-locomo.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-lp55xx-common.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-lp55xx-common.h treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-lp3944.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-lp3952.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-lp5521.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 336 2019-06-05 17:37:07 +02:00
leds-lp5523.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 336 2019-06-05 17:37:07 +02:00
leds-lp5562.c leds: leds-lp5562 allow firmware files up to the maximum length 2019-07-22 20:35:01 +02:00
leds-lp8501.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-lp8788.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-lp8860.c leds: lp8860: Use generic support for composing LED names 2019-07-25 20:07:55 +02:00
leds-lt3593.c leds: lt3593: Use generic support for composing LED names 2019-07-25 20:07:54 +02:00
leds-max8997.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-max77650.c led: max77650: add of_match table 2020-02-05 21:22:49 +00:00
leds-max77693.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-mc13783.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-menf21bmc.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 152 2019-05-30 11:26:32 -07:00
leds-mlxcpld.c treewide: devm_kzalloc() -> devm_kcalloc() 2018-06-12 16:19:22 -07:00
leds-mlxreg.c leds: mlxreg: Fix possible buffer overflow 2020-10-01 13:17:14 +02:00
leds-mt6323.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 157 2019-05-30 11:26:37 -07:00
leds-net48xx.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-netxbig.c leds: netxbig: Add of_node_put() in netxbig_leds_get_of_pdata() 2019-07-29 21:12:54 +02:00
leds-nic78bx.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 157 2019-05-30 11:26:37 -07:00
leds-ns2.c leds: ns2: Add of_node_put() before return 2019-07-22 20:35:00 +02:00
leds-ot200.c leds: leds-ot200: Use devm_led_classdev_register 2015-11-03 08:59:13 +01:00
leds-pca955x.c LED updates for 5.3-rc1 2019-07-09 08:59:39 -07:00
leds-pca963x.c leds: pca963x: Fix open-drain initialization 2020-02-24 08:36:24 +01:00
leds-pca9532.c leds: pca953x: Include the right header 2019-08-08 20:38:24 +02:00
leds-pm8058.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 284 2019-06-05 17:36:37 +02:00
leds-powernv.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 152 2019-05-30 11:26:32 -07:00
leds-pwm.c leds: pwm: Use struct_size() helper 2019-09-01 13:33:29 +02:00
leds-rb532.c treewide: Add SPDX license identifier for more missed files 2019-05-21 10:50:45 +02:00
leds-regulator.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-s3c24xx.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-sc27xx-bltc.c leds: sc27xx-blt: Use generic support for composing LED names 2019-07-25 20:07:53 +02:00
leds-spi-byte.c leds: spi-byte: add single byte SPI LED driver 2019-05-24 22:19:43 +02:00
leds-ss4200.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 335 2019-06-05 17:37:06 +02:00
leds-sunfire.c treewide: Add SPDX license identifier for more missed files 2019-05-21 10:50:45 +02:00
leds-syscon.c leds: syscon: Use resource managed variant of device register 2019-09-01 13:33:27 +02:00
leds-tca6507.c LED updates for 5.3-rc1 2019-07-09 08:59:39 -07:00
leds-ti-lmu-common.c leds: ti-lmu-common: Move static keyword to the front of declaration 2019-09-03 20:34:46 +02:00
leds-tlc591xx.c leds: tlc591xx: update the maximum brightness 2020-01-26 10:01:02 +01:00
leds-wm831x-status.c leds: wm831x-status: fix use-after-free on unbind 2020-08-11 15:33:35 +02:00
leds-wm8350.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds-wrap.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500 2019-06-19 17:09:55 +02:00
leds.h leds: core: Add support for composing LED class device names 2019-07-25 20:07:52 +02:00
Makefile TI LMU LED support rework and introduction of two new drivers 2019-06-11 23:46:37 +02:00
uleds.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 157 2019-05-30 11:26:37 -07:00