Skip to content

Commit 476e44d

Browse files
author
Bartosz Golaszewski
committed
gpio: shared: fix a race condition
When matching the reset-gpio reference with the actual firmware node consuming the GPIO, we also need to lock the structure associated with the latter as it can change while we're doing it. Due to triggering lockdep false-positives, we need to use a per-reference lockdep class but accidentally, this also allows us to remove the previous lockdep workaround for cleaner code. Fixes: 4941648 ("gpio: shared: allow sharing a reset-gpios pin between reset-gpio and gpiolib") Reported-by: Marek Szyprowski <m.szyprowski@samsung.com> Closes: https://lore.kernel.org/all/00107523-7737-4b92-a785-14ce4e93b8cb@samsung.com/ Tested-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20260106-gpio-shared-fixes-v2-2-c7091d2f7581@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
1 parent 0fe5063 commit 476e44d

1 file changed

Lines changed: 9 additions & 11 deletions

File tree

drivers/gpio/gpiolib-shared.c

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct gpio_shared_ref {
3838
int dev_id;
3939
/* Protects the auxiliary device struct and the lookup table. */
4040
struct mutex lock;
41+
struct lock_class_key lock_key;
4142
struct auxiliary_device adev;
4243
struct gpiod_lookup_table *lookup;
4344
};
@@ -99,7 +100,8 @@ static struct gpio_shared_ref *gpio_shared_make_ref(struct fwnode_handle *fwnode
99100
ref->flags = flags;
100101
ref->con_id = no_free_ptr(con_id_cpy);
101102
ref->fwnode = fwnode;
102-
mutex_init(&ref->lock);
103+
lockdep_register_key(&ref->lock_key);
104+
mutex_init_with_key(&ref->lock, &ref->lock_key);
103105

104106
return no_free_ptr(ref);
105107
}
@@ -378,6 +380,11 @@ static bool gpio_shared_dev_is_reset_gpio(struct device *consumer,
378380
* arguments match the ones from this consumer's node.
379381
*/
380382
list_for_each_entry(real_ref, &entry->refs, list) {
383+
if (real_ref == ref)
384+
continue;
385+
386+
guard(mutex)(&real_ref->lock);
387+
381388
if (!real_ref->fwnode)
382389
continue;
383390

@@ -568,15 +575,6 @@ void gpio_device_teardown_shared(struct gpio_device *gdev)
568575
if (!device_match_fwnode(&gdev->dev, entry->fwnode))
569576
continue;
570577

571-
/*
572-
* For some reason if we call synchronize_srcu() in GPIO core,
573-
* descent here and take this mutex and then recursively call
574-
* synchronize_srcu() again from gpiochip_remove() (which is
575-
* totally fine) called after gpio_shared_remove_adev(),
576-
* lockdep prints a false positive deadlock splat. Disable
577-
* lockdep here.
578-
*/
579-
lockdep_off();
580578
list_for_each_entry(ref, &entry->refs, list) {
581579
guard(mutex)(&ref->lock);
582580

@@ -589,7 +587,6 @@ void gpio_device_teardown_shared(struct gpio_device *gdev)
589587

590588
gpio_shared_remove_adev(&ref->adev);
591589
}
592-
lockdep_on();
593590
}
594591
}
595592

@@ -685,6 +682,7 @@ static void gpio_shared_drop_ref(struct gpio_shared_ref *ref)
685682
{
686683
list_del(&ref->list);
687684
mutex_destroy(&ref->lock);
685+
lockdep_unregister_key(&ref->lock_key);
688686
kfree(ref->con_id);
689687
ida_free(&gpio_shared_ida, ref->dev_id);
690688
fwnode_handle_put(ref->fwnode);

0 commit comments

Comments
 (0)