Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72eba6f6 authored by Johan Hovold's avatar Johan Hovold Committed by Linus Walleij
Browse files

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: default avatarJohan Hovold <johan@kernel.org>
Reviewed-by: default avatarAlexandre Courbot <acourbot@nvidia.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 6ffcb797
Loading
Loading
Loading
Loading
+26 −25
Original line number Diff line number Diff line
@@ -675,8 +675,7 @@ 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 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) {
	if (!dev)
		goto err_unlock;

	data = dev_get_drvdata(dev);

	clear_bit(FLAG_SYSFS_DIR, &desc->flags);
	clear_bit(FLAG_EXPORT, &desc->flags);
		} else
			status = -ENODEV;
	}

	mutex_unlock(&sysfs_lock);

	if (dev) {
		data = dev_get_drvdata(dev);
	device_unregister(dev);

	/*
		 * Release irq after deregistration to prevent race with
		 * edge_store.
	 * 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);

	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);