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

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

gpio: sysfs: fix gpio attribute-creation race



Fix attribute-creation race with userspace by using the default group
to create also the contingent gpio device attributes.

Fixes: d8f388d8 ("gpio: sysfs interface")
Signed-off-by: default avatarJohan Hovold <johan@kernel.org>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 0915e6fe
Loading
Loading
Loading
Loading
+39 −22
Original line number Diff line number Diff line
@@ -356,12 +356,44 @@ static ssize_t gpio_active_low_store(struct device *dev,
static DEVICE_ATTR(active_low, 0644,
		gpio_active_low_show, gpio_active_low_store);

static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,
			       int n)
{
	struct device *dev = container_of(kobj, struct device, kobj);
	struct gpio_desc *desc = dev_get_drvdata(dev);
	umode_t mode = attr->mode;
	bool show_direction = test_bit(FLAG_SYSFS_DIR, &desc->flags);

	if (attr == &dev_attr_direction.attr) {
		if (!show_direction)
			mode = 0;
	} else if (attr == &dev_attr_edge.attr) {
		if (gpiod_to_irq(desc) < 0)
			mode = 0;
		if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags))
			mode = 0;
	}

	return mode;
}

static struct attribute *gpio_attrs[] = {
	&dev_attr_direction.attr,
	&dev_attr_edge.attr,
	&dev_attr_value.attr,
	&dev_attr_active_low.attr,
	NULL,
};
ATTRIBUTE_GROUPS(gpio);

static const struct attribute_group gpio_group = {
	.attrs = gpio_attrs,
	.is_visible = gpio_is_visible,
};

static const struct attribute_group *gpio_groups[] = {
	&gpio_group,
	NULL
};

/*
 * /sys/class/gpio/gpiochipN/
@@ -550,8 +582,11 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
		goto fail_unlock;
	}

	if (!desc->chip->direction_input || !desc->chip->direction_output)
		direction_may_change = false;
	if (desc->chip->direction_input && desc->chip->direction_output &&
			direction_may_change) {
		set_bit(FLAG_SYSFS_DIR, &desc->flags);
	}

	spin_unlock_irqrestore(&gpio_lock, flags);

	offset = gpio_chip_hwgpio(desc);
@@ -567,27 +602,10 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
		goto fail_unlock;
	}

	if (direction_may_change) {
		status = device_create_file(dev, &dev_attr_direction);
		if (status)
			goto fail_unregister_device;
	}

	if (gpiod_to_irq(desc) >= 0 && (direction_may_change ||
				       !test_bit(FLAG_IS_OUT, &desc->flags))) {
		status = device_create_file(dev, &dev_attr_edge);
		if (status)
			goto fail_remove_attr_direction;
	}

	set_bit(FLAG_EXPORT, &desc->flags);
	mutex_unlock(&sysfs_lock);
	return 0;

fail_remove_attr_direction:
	device_remove_file(dev, &dev_attr_direction);
fail_unregister_device:
	device_unregister(dev);
fail_unlock:
	mutex_unlock(&sysfs_lock);
	gpiod_dbg(desc, "%s: status %d\n", __func__, status);
@@ -711,6 +729,7 @@ void gpiod_unexport(struct gpio_desc *desc)
		dev = class_find_device(&gpio_class, NULL, desc, match_export);
		if (dev) {
			gpio_setup_irq(desc, dev, 0);
			clear_bit(FLAG_SYSFS_DIR, &desc->flags);
			clear_bit(FLAG_EXPORT, &desc->flags);
		} else
			status = -ENODEV;
@@ -719,8 +738,6 @@ void gpiod_unexport(struct gpio_desc *desc)
	mutex_unlock(&sysfs_lock);

	if (dev) {
		device_remove_file(dev, &dev_attr_edge);
		device_remove_file(dev, &dev_attr_direction);
		device_unregister(dev);
		put_device(dev);
	}
+1 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ struct gpio_desc {
#define FLAG_OPEN_DRAIN	7	/* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8	/* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9	/* GPIO is connected to an IRQ */
#define FLAG_SYSFS_DIR	10	/* show sysfs direction attribute */

#define ID_SHIFT	16	/* add new flags before this one */