Commit 58409c4e authored by Johan Hovold's avatar Johan Hovold Committed by Luis Henriques

gpio: sysfs: fix gpio attribute-creation race

commit ebbeba12 upstream.

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>
[ luis: backported to 3.16:
  - all changes in drivers/gpio/gpiolib.c
  - adjusted context ]
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 11fd056b
...@@ -60,6 +60,7 @@ struct gpio_desc { ...@@ -60,6 +60,7 @@ struct gpio_desc {
#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source 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_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 */ #define ID_SHIFT 16 /* add new flags before this one */
...@@ -637,12 +638,44 @@ static ssize_t gpio_active_low_store(struct device *dev, ...@@ -637,12 +638,44 @@ static ssize_t gpio_active_low_store(struct device *dev,
static DEVICE_ATTR(active_low, 0644, static DEVICE_ATTR(active_low, 0644,
gpio_active_low_show, gpio_active_low_store); 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[] = { static struct attribute *gpio_attrs[] = {
&dev_attr_direction.attr,
&dev_attr_edge.attr,
&dev_attr_value.attr, &dev_attr_value.attr,
&dev_attr_active_low.attr, &dev_attr_active_low.attr,
NULL, 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/ * /sys/class/gpio/gpiochipN/
...@@ -831,8 +864,11 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ...@@ -831,8 +864,11 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
goto fail_unlock; goto fail_unlock;
} }
if (!desc->chip->direction_input || !desc->chip->direction_output) if (desc->chip->direction_input && desc->chip->direction_output &&
direction_may_change = false; direction_may_change) {
set_bit(FLAG_SYSFS_DIR, &desc->flags);
}
spin_unlock_irqrestore(&gpio_lock, flags); spin_unlock_irqrestore(&gpio_lock, flags);
offset = gpio_chip_hwgpio(desc); offset = gpio_chip_hwgpio(desc);
...@@ -848,27 +884,10 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ...@@ -848,27 +884,10 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
goto fail_unlock; 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); set_bit(FLAG_EXPORT, &desc->flags);
mutex_unlock(&sysfs_lock); mutex_unlock(&sysfs_lock);
return 0; return 0;
fail_remove_attr_direction:
device_remove_file(dev, &dev_attr_direction);
fail_unregister_device:
device_unregister(dev);
fail_unlock: fail_unlock:
mutex_unlock(&sysfs_lock); mutex_unlock(&sysfs_lock);
gpiod_dbg(desc, "%s: status %d\n", __func__, status); gpiod_dbg(desc, "%s: status %d\n", __func__, status);
...@@ -994,6 +1013,7 @@ void gpiod_unexport(struct gpio_desc *desc) ...@@ -994,6 +1013,7 @@ void gpiod_unexport(struct gpio_desc *desc)
dev = class_find_device(&gpio_class, NULL, desc, match_export); dev = class_find_device(&gpio_class, NULL, desc, match_export);
if (dev) { if (dev) {
gpio_setup_irq(desc, dev, 0); gpio_setup_irq(desc, dev, 0);
clear_bit(FLAG_SYSFS_DIR, &desc->flags);
clear_bit(FLAG_EXPORT, &desc->flags); clear_bit(FLAG_EXPORT, &desc->flags);
} else } else
status = -ENODEV; status = -ENODEV;
...@@ -1002,8 +1022,6 @@ void gpiod_unexport(struct gpio_desc *desc) ...@@ -1002,8 +1022,6 @@ void gpiod_unexport(struct gpio_desc *desc)
mutex_unlock(&sysfs_lock); mutex_unlock(&sysfs_lock);
if (dev) { if (dev) {
device_remove_file(dev, &dev_attr_edge);
device_remove_file(dev, &dev_attr_direction);
device_unregister(dev); device_unregister(dev);
put_device(dev); put_device(dev);
} }
......
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