Commit 65a828ba authored by Bartosz Golaszewski's avatar Bartosz Golaszewski

gpiolib: use a mutex to protect the list of GPIO devices

The global list of GPIO devices is never modified or accessed from
atomic context so it's fine to protect it using a mutex. Add a new
global lock dedicated to the gpio_devices list and use it whenever
accessing or modifying it.
Signed-off-by: default avatarBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
parent f95fd4ac
...@@ -766,6 +766,25 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) ...@@ -766,6 +766,25 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
return 0; return 0;
} }
int gpiochip_sysfs_register_all(void)
{
struct gpio_device *gdev;
int ret;
guard(mutex)(&gpio_devices_lock);
list_for_each_entry(gdev, &gpio_devices, list) {
if (gdev->mockdev)
continue;
ret = gpiochip_sysfs_register(gdev);
if (ret)
return ret;
}
return 0;
}
void gpiochip_sysfs_unregister(struct gpio_device *gdev) void gpiochip_sysfs_unregister(struct gpio_device *gdev)
{ {
struct gpio_desc *desc; struct gpio_desc *desc;
...@@ -791,8 +810,6 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) ...@@ -791,8 +810,6 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
static int __init gpiolib_sysfs_init(void) static int __init gpiolib_sysfs_init(void)
{ {
int status; int status;
unsigned long flags;
struct gpio_device *gdev;
status = class_register(&gpio_class); status = class_register(&gpio_class);
if (status < 0) if (status < 0)
...@@ -804,26 +821,6 @@ static int __init gpiolib_sysfs_init(void) ...@@ -804,26 +821,6 @@ static int __init gpiolib_sysfs_init(void)
* We run before arch_initcall() so chip->dev nodes can have * We run before arch_initcall() so chip->dev nodes can have
* registered, and so arch_initcall() can always gpiod_export(). * registered, and so arch_initcall() can always gpiod_export().
*/ */
spin_lock_irqsave(&gpio_lock, flags); return gpiochip_sysfs_register_all();
list_for_each_entry(gdev, &gpio_devices, list) {
if (gdev->mockdev)
continue;
/*
* TODO we yield gpio_lock here because
* gpiochip_sysfs_register() acquires a mutex. This is unsafe
* and needs to be fixed.
*
* Also it would be nice to use gpio_device_find() here so we
* can keep gpio_chips local to gpiolib.c, but the yield of
* gpio_lock prevents us from doing this.
*/
spin_unlock_irqrestore(&gpio_lock, flags);
status = gpiochip_sysfs_register(gdev);
spin_lock_irqsave(&gpio_lock, flags);
}
spin_unlock_irqrestore(&gpio_lock, flags);
return status;
} }
postcore_initcall(gpiolib_sysfs_init); postcore_initcall(gpiolib_sysfs_init);
...@@ -8,6 +8,7 @@ struct gpio_device; ...@@ -8,6 +8,7 @@ struct gpio_device;
#ifdef CONFIG_GPIO_SYSFS #ifdef CONFIG_GPIO_SYSFS
int gpiochip_sysfs_register(struct gpio_device *gdev); int gpiochip_sysfs_register(struct gpio_device *gdev);
int gpiochip_sysfs_register_all(void);
void gpiochip_sysfs_unregister(struct gpio_device *gdev); void gpiochip_sysfs_unregister(struct gpio_device *gdev);
#else #else
...@@ -17,6 +18,11 @@ static inline int gpiochip_sysfs_register(struct gpio_device *gdev) ...@@ -17,6 +18,11 @@ static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
return 0; return 0;
} }
static inline int gpiochip_sysfs_register_all(void)
{
return 0;
}
static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev) static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
{ {
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/cleanup.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -15,6 +16,7 @@ ...@@ -15,6 +16,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
...@@ -94,7 +96,9 @@ DEFINE_SPINLOCK(gpio_lock); ...@@ -94,7 +96,9 @@ DEFINE_SPINLOCK(gpio_lock);
static DEFINE_MUTEX(gpio_lookup_lock); static DEFINE_MUTEX(gpio_lookup_lock);
static LIST_HEAD(gpio_lookup_list); static LIST_HEAD(gpio_lookup_list);
LIST_HEAD(gpio_devices); LIST_HEAD(gpio_devices);
DEFINE_MUTEX(gpio_devices_lock);
static DEFINE_MUTEX(gpio_machine_hogs_mutex); static DEFINE_MUTEX(gpio_machine_hogs_mutex);
static LIST_HEAD(gpio_machine_hogs); static LIST_HEAD(gpio_machine_hogs);
...@@ -126,20 +130,15 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) ...@@ -126,20 +130,15 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
struct gpio_desc *gpio_to_desc(unsigned gpio) struct gpio_desc *gpio_to_desc(unsigned gpio)
{ {
struct gpio_device *gdev; struct gpio_device *gdev;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
scoped_guard(mutex, &gpio_devices_lock) {
list_for_each_entry(gdev, &gpio_devices, list) { list_for_each_entry(gdev, &gpio_devices, list) {
if (gdev->base <= gpio && if (gdev->base <= gpio &&
gdev->base + gdev->ngpio > gpio) { gdev->base + gdev->ngpio > gpio)
spin_unlock_irqrestore(&gpio_lock, flags);
return &gdev->descs[gpio - gdev->base]; return &gdev->descs[gpio - gdev->base];
} }
} }
spin_unlock_irqrestore(&gpio_lock, flags);
if (!gpio_is_valid(gpio)) if (!gpio_is_valid(gpio))
pr_warn("invalid GPIO %d\n", gpio); pr_warn("invalid GPIO %d\n", gpio);
...@@ -412,25 +411,20 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev) ...@@ -412,25 +411,20 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev)
static struct gpio_desc *gpio_name_to_desc(const char * const name) static struct gpio_desc *gpio_name_to_desc(const char * const name)
{ {
struct gpio_device *gdev; struct gpio_device *gdev;
unsigned long flags;
if (!name) if (!name)
return NULL; return NULL;
spin_lock_irqsave(&gpio_lock, flags); guard(mutex)(&gpio_devices_lock);
list_for_each_entry(gdev, &gpio_devices, list) { list_for_each_entry(gdev, &gpio_devices, list) {
struct gpio_desc *desc; struct gpio_desc *desc;
for_each_gpio_desc(gdev->chip, desc) { for_each_gpio_desc(gdev->chip, desc) {
if (desc->name && !strcmp(desc->name, name)) { if (desc->name && !strcmp(desc->name, name))
spin_unlock_irqrestore(&gpio_lock, flags);
return desc; return desc;
} }
} }
}
spin_unlock_irqrestore(&gpio_lock, flags);
return NULL; return NULL;
} }
...@@ -669,11 +663,9 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid); ...@@ -669,11 +663,9 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
static void gpiodev_release(struct device *dev) static void gpiodev_release(struct device *dev)
{ {
struct gpio_device *gdev = to_gpio_device(dev); struct gpio_device *gdev = to_gpio_device(dev);
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags); scoped_guard(mutex, &gpio_devices_lock)
list_del(&gdev->list); list_del(&gdev->list);
spin_unlock_irqrestore(&gpio_lock, flags);
ida_free(&gpio_ida, gdev->id); ida_free(&gpio_ida, gdev->id);
kfree_const(gdev->label); kfree_const(gdev->label);
...@@ -831,7 +823,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ...@@ -831,7 +823,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
struct lock_class_key *request_key) struct lock_class_key *request_key)
{ {
struct gpio_device *gdev; struct gpio_device *gdev;
unsigned long flags;
unsigned int i; unsigned int i;
int base = 0; int base = 0;
int ret = 0; int ret = 0;
...@@ -896,8 +887,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ...@@ -896,8 +887,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
gdev->ngpio = gc->ngpio; gdev->ngpio = gc->ngpio;
spin_lock_irqsave(&gpio_lock, flags); scoped_guard(mutex, &gpio_devices_lock) {
/* /*
* TODO: this allocates a Linux GPIO number base in the global * TODO: this allocates a Linux GPIO number base in the global
* GPIO numberspace for this chip. In the long run we want to * GPIO numberspace for this chip. In the long run we want to
...@@ -906,10 +896,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ...@@ -906,10 +896,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
* of the sysfs interface anyways. * of the sysfs interface anyways.
*/ */
base = gc->base; base = gc->base;
if (base < 0) { if (base < 0) {
base = gpiochip_find_base_unlocked(gc->ngpio); base = gpiochip_find_base_unlocked(gc->ngpio);
if (base < 0) { if (base < 0) {
spin_unlock_irqrestore(&gpio_lock, flags);
ret = base; ret = base;
base = 0; base = 0;
goto err_free_label; goto err_free_label;
...@@ -929,15 +919,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ...@@ -929,15 +919,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
ret = gpiodev_add_to_list_unlocked(gdev); ret = gpiodev_add_to_list_unlocked(gdev);
if (ret) { if (ret) {
spin_unlock_irqrestore(&gpio_lock, flags);
chip_err(gc, "GPIO integer space overlap, cannot add chip\n"); chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
goto err_free_label; goto err_free_label;
} }
for (i = 0; i < gc->ngpio; i++) for (i = 0; i < gc->ngpio; i++)
gdev->descs[i].gdev = gdev; gdev->descs[i].gdev = gdev;
}
spin_unlock_irqrestore(&gpio_lock, flags);
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
...@@ -1029,9 +1017,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ...@@ -1029,9 +1017,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
goto err_print_message; goto err_print_message;
} }
err_remove_from_list: err_remove_from_list:
spin_lock_irqsave(&gpio_lock, flags); scoped_guard(mutex, &gpio_devices_lock)
list_del(&gdev->list); list_del(&gdev->list);
spin_unlock_irqrestore(&gpio_lock, flags);
err_free_label: err_free_label:
kfree_const(gdev->label); kfree_const(gdev->label);
err_free_descs: err_free_descs:
...@@ -1140,7 +1127,7 @@ struct gpio_device *gpio_device_find(void *data, ...@@ -1140,7 +1127,7 @@ struct gpio_device *gpio_device_find(void *data,
*/ */
might_sleep(); might_sleep();
guard(spinlock_irqsave)(&gpio_lock); guard(mutex)(&gpio_devices_lock);
list_for_each_entry(gdev, &gpio_devices, list) { list_for_each_entry(gdev, &gpio_devices, list) {
if (gdev->chip && match(gdev->chip, data)) if (gdev->chip && match(gdev->chip, data))
...@@ -4756,35 +4743,33 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) ...@@ -4756,35 +4743,33 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
{ {
unsigned long flags;
struct gpio_device *gdev = NULL; struct gpio_device *gdev = NULL;
loff_t index = *pos; loff_t index = *pos;
s->private = ""; s->private = "";
spin_lock_irqsave(&gpio_lock, flags); guard(mutex)(&gpio_devices_lock);
list_for_each_entry(gdev, &gpio_devices, list)
if (index-- == 0) { list_for_each_entry(gdev, &gpio_devices, list) {
spin_unlock_irqrestore(&gpio_lock, flags); if (index-- == 0)
return gdev; return gdev;
} }
spin_unlock_irqrestore(&gpio_lock, flags);
return NULL; return NULL;
} }
static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
{ {
unsigned long flags;
struct gpio_device *gdev = v; struct gpio_device *gdev = v;
void *ret = NULL; void *ret = NULL;
spin_lock_irqsave(&gpio_lock, flags); scoped_guard(mutex, &gpio_devices_lock) {
if (list_is_last(&gdev->list, &gpio_devices)) if (list_is_last(&gdev->list, &gpio_devices))
ret = NULL; ret = NULL;
else else
ret = list_first_entry(&gdev->list, struct gpio_device, list); ret = list_first_entry(&gdev->list, struct gpio_device,
spin_unlock_irqrestore(&gpio_lock, flags); list);
}
s->private = "\n"; s->private = "\n";
++*pos; ++*pos;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/gpio/consumer.h> /* for enum gpiod_flags */ #include <linux/gpio/consumer.h> /* for enum gpiod_flags */
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
...@@ -136,6 +137,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); ...@@ -136,6 +137,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
extern spinlock_t gpio_lock; extern spinlock_t gpio_lock;
extern struct list_head gpio_devices; extern struct list_head gpio_devices;
extern struct mutex gpio_devices_lock;
void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment