diff options
author | Johan Hovold <johan@kernel.org> | 2015-05-04 17:10:45 +0200 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2015-05-12 10:47:44 +0200 |
commit | 72eba6f66a0017356380eec64db8780305aee2b8 (patch) | |
tree | 960f5d1486f52fff62c687dcbbcf9d9ce650c01a /drivers/gpio/gpiolib-sysfs.c | |
parent | 6ffcb7971486ea4f1eb14f07f8efb0b6f829a23c (diff) |
gpio: sysfs: fix race between gpiod export and unexport
Make sure to deregister the class device (and release the irq) while
holding the sysfs lock in gpio_unexport to prevent racing with
gpio_export.
Note that this requires the recently introduced per-gpio locking to
avoid a deadlock with the kernfs active protection when waiting for the
attribute operations to drain during deregistration.
Signed-off-by: Johan Hovold <johan@kernel.org>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio/gpiolib-sysfs.c')
-rw-r--r-- | drivers/gpio/gpiolib-sysfs.c | 51 |
1 files changed, 26 insertions, 25 deletions
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 1bb05aa33a84..1540f7d60f06 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -674,9 +674,8 @@ EXPORT_SYMBOL_GPL(gpiod_export_link); */ void gpiod_unexport(struct gpio_desc *desc) { - struct gpiod_data *data; - int status = 0; - struct device *dev = NULL; + struct gpiod_data *data; + struct device *dev; if (!desc) { pr_warn("%s: invalid GPIO\n", __func__); @@ -685,33 +684,35 @@ void gpiod_unexport(struct gpio_desc *desc) mutex_lock(&sysfs_lock); - if (test_bit(FLAG_EXPORT, &desc->flags)) { + if (!test_bit(FLAG_EXPORT, &desc->flags)) + goto err_unlock; - dev = class_find_device(&gpio_class, NULL, desc, match_export); - if (dev) { - clear_bit(FLAG_SYSFS_DIR, &desc->flags); - clear_bit(FLAG_EXPORT, &desc->flags); - } else - status = -ENODEV; - } + dev = class_find_device(&gpio_class, NULL, desc, match_export); + if (!dev) + goto err_unlock; + + data = dev_get_drvdata(dev); + + clear_bit(FLAG_SYSFS_DIR, &desc->flags); + clear_bit(FLAG_EXPORT, &desc->flags); + + device_unregister(dev); + + /* + * Release irq after deregistration to prevent race with edge_store. + */ + if (desc->flags & GPIO_TRIGGER_MASK) + gpio_sysfs_free_irq(dev); mutex_unlock(&sysfs_lock); - if (dev) { - data = dev_get_drvdata(dev); - device_unregister(dev); - /* - * Release irq after deregistration to prevent race with - * edge_store. - */ - if (desc->flags & GPIO_TRIGGER_MASK) - gpio_sysfs_free_irq(dev); - put_device(dev); - kfree(data); - } + put_device(dev); + kfree(data); - if (status) - gpiod_dbg(desc, "%s: status %d\n", __func__, status); + return; + +err_unlock: + mutex_unlock(&sysfs_lock); } EXPORT_SYMBOL_GPL(gpiod_unexport); |