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: d8f388d8dc
("gpio: sysfs interface")
Signed-off-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
parent
0915e6feb3
commit
ebbeba120a
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
Loading…
Reference in New Issue