enum gpiod_flags flags;
char *con_id;
int dev_id;
+ /* Protects the auxiliary device struct and the lookup table. */
+ struct mutex lock;
struct auxiliary_device adev;
struct gpiod_lookup_table *lookup;
};
unsigned int offset;
/* Index in the property value array. */
size_t index;
+ /* Synchronizes the modification of shared_desc. */
struct mutex lock;
struct gpio_shared_desc *shared_desc;
struct kref ref;
};
static LIST_HEAD(gpio_shared_list);
-static DEFINE_MUTEX(gpio_shared_lock);
static DEFINE_IDA(gpio_shared_ida);
#if IS_ENABLED(CONFIG_OF)
ref->fwnode = fwnode_handle_get(of_fwnode_handle(curr));
ref->flags = args.args[1];
+ mutex_init(&ref->lock);
if (strends(prop->name, "gpios"))
suffix = "-gpios";
struct auxiliary_device *adev = &ref->adev;
int ret;
- lockdep_assert_held(&gpio_shared_lock);
+ guard(mutex)(&ref->lock);
memset(adev, 0, sizeof(*adev));
if (!lookup)
return -ENOMEM;
- guard(mutex)(&gpio_shared_lock);
-
list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) {
if (!device_match_fwnode(consumer, ref->fwnode) &&
!gpio_shared_dev_is_reset_gpio(consumer, entry, ref))
continue;
+ guard(mutex)(&ref->lock);
+
/* We've already done that on a previous request. */
if (ref->lookup)
return 0;
static void gpio_shared_remove_adev(struct auxiliary_device *adev)
{
- lockdep_assert_held(&gpio_shared_lock);
-
auxiliary_device_delete(adev);
auxiliary_device_uninit(adev);
}
unsigned long *flags;
int ret;
- guard(mutex)(&gpio_shared_lock);
-
list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) {
if (gdev->dev.parent == &ref->adev.dev) {
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
- guard(mutex)(&gpio_shared_lock);
-
list_for_each_entry(entry, &gpio_shared_list, list) {
if (!device_match_fwnode(&gdev->dev, entry->fwnode))
continue;
+ /*
+ * For some reason if we call synchronize_srcu() in GPIO core,
+ * descent here and take this mutex and then recursively call
+ * synchronize_srcu() again from gpiochip_remove() (which is
+ * totally fine) called after gpio_shared_remove_adev(),
+ * lockdep prints a false positive deadlock splat. Disable
+ * lockdep here.
+ */
+ lockdep_off();
list_for_each_entry(ref, &entry->refs, list) {
+ guard(mutex)(&ref->lock);
+
if (ref->lookup) {
gpiod_remove_lookup_table(ref->lookup);
kfree(ref->lookup->table[0].key);
gpio_shared_remove_adev(&ref->adev);
}
+ lockdep_on();
}
}
{
struct gpio_shared_entry *entry = data;
- lockdep_assert_not_held(&gpio_shared_lock);
-
kref_put(&entry->ref, gpio_shared_release);
}
struct gpio_shared_entry *entry;
int ret;
- lockdep_assert_not_held(&gpio_shared_lock);
-
entry = dev_get_platdata(dev);
if (WARN_ON(!entry))
/* Programmer bug */
static void gpio_shared_drop_ref(struct gpio_shared_ref *ref)
{
list_del(&ref->list);
+ mutex_destroy(&ref->lock);
kfree(ref->con_id);
ida_free(&gpio_shared_ida, ref->dev_id);
fwnode_handle_put(ref->fwnode);