diff options
author | Johan Hovold <johan@kernel.org> | 2015-01-13 07:00:06 -0500 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2015-01-15 11:20:56 -0500 |
commit | ebbeba120ab2ec6ac5f3afc1425ec6ff0b77ad6f (patch) | |
tree | 34ced1282b4b34b9ee79e9e0549dbca3ade7aab6 /drivers/gpio/gpiolib-sysfs.c | |
parent | 0915e6feb38de8d3601819992a5bd050201a56fa (diff) |
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: d8f388d8dc8d ("gpio: sysfs interface")
Signed-off-by: Johan Hovold <johan@kernel.org>
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 | 61 |
1 files changed, 39 insertions, 22 deletions
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index fd4d9423f6e6..f62aa115d79a 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c | |||
@@ -356,12 +356,44 @@ static ssize_t gpio_active_low_store(struct device *dev, | |||
356 | static DEVICE_ATTR(active_low, 0644, | 356 | static DEVICE_ATTR(active_low, 0644, |
357 | gpio_active_low_show, gpio_active_low_store); | 357 | gpio_active_low_show, gpio_active_low_store); |
358 | 358 | ||
359 | static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, | ||
360 | int n) | ||
361 | { | ||
362 | struct device *dev = container_of(kobj, struct device, kobj); | ||
363 | struct gpio_desc *desc = dev_get_drvdata(dev); | ||
364 | umode_t mode = attr->mode; | ||
365 | bool show_direction = test_bit(FLAG_SYSFS_DIR, &desc->flags); | ||
366 | |||
367 | if (attr == &dev_attr_direction.attr) { | ||
368 | if (!show_direction) | ||
369 | mode = 0; | ||
370 | } else if (attr == &dev_attr_edge.attr) { | ||
371 | if (gpiod_to_irq(desc) < 0) | ||
372 | mode = 0; | ||
373 | if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags)) | ||
374 | mode = 0; | ||
375 | } | ||
376 | |||
377 | return mode; | ||
378 | } | ||
379 | |||
359 | static struct attribute *gpio_attrs[] = { | 380 | static struct attribute *gpio_attrs[] = { |
381 | &dev_attr_direction.attr, | ||
382 | &dev_attr_edge.attr, | ||
360 | &dev_attr_value.attr, | 383 | &dev_attr_value.attr, |
361 | &dev_attr_active_low.attr, | 384 | &dev_attr_active_low.attr, |
362 | NULL, | 385 | NULL, |
363 | }; | 386 | }; |
364 | ATTRIBUTE_GROUPS(gpio); | 387 | |
388 | static const struct attribute_group gpio_group = { | ||
389 | .attrs = gpio_attrs, | ||
390 | .is_visible = gpio_is_visible, | ||
391 | }; | ||
392 | |||
393 | static const struct attribute_group *gpio_groups[] = { | ||
394 | &gpio_group, | ||
395 | NULL | ||
396 | }; | ||
365 | 397 | ||
366 | /* | 398 | /* |
367 | * /sys/class/gpio/gpiochipN/ | 399 | * /sys/class/gpio/gpiochipN/ |
@@ -550,8 +582,11 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) | |||
550 | goto fail_unlock; | 582 | goto fail_unlock; |
551 | } | 583 | } |
552 | 584 | ||
553 | if (!desc->chip->direction_input || !desc->chip->direction_output) | 585 | if (desc->chip->direction_input && desc->chip->direction_output && |
554 | direction_may_change = false; | 586 | direction_may_change) { |
587 | set_bit(FLAG_SYSFS_DIR, &desc->flags); | ||
588 | } | ||
589 | |||
555 | spin_unlock_irqrestore(&gpio_lock, flags); | 590 | spin_unlock_irqrestore(&gpio_lock, flags); |
556 | 591 | ||
557 | offset = gpio_chip_hwgpio(desc); | 592 | offset = gpio_chip_hwgpio(desc); |
@@ -567,27 +602,10 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) | |||
567 | goto fail_unlock; | 602 | goto fail_unlock; |
568 | } | 603 | } |
569 | 604 | ||
570 | if (direction_may_change) { | ||
571 | status = device_create_file(dev, &dev_attr_direction); | ||
572 | if (status) | ||
573 | goto fail_unregister_device; | ||
574 | } | ||
575 | |||
576 | if (gpiod_to_irq(desc) >= 0 && (direction_may_change || | ||
577 | !test_bit(FLAG_IS_OUT, &desc->flags))) { | ||
578 | status = device_create_file(dev, &dev_attr_edge); | ||
579 | if (status) | ||
580 | goto fail_remove_attr_direction; | ||
581 | } | ||
582 | |||
583 | set_bit(FLAG_EXPORT, &desc->flags); | 605 | set_bit(FLAG_EXPORT, &desc->flags); |
584 | mutex_unlock(&sysfs_lock); | 606 | mutex_unlock(&sysfs_lock); |
585 | return 0; | 607 | return 0; |
586 | 608 | ||
587 | fail_remove_attr_direction: | ||
588 | device_remove_file(dev, &dev_attr_direction); | ||
589 | fail_unregister_device: | ||
590 | device_unregister(dev); | ||
591 | fail_unlock: | 609 | fail_unlock: |
592 | mutex_unlock(&sysfs_lock); | 610 | mutex_unlock(&sysfs_lock); |
593 | gpiod_dbg(desc, "%s: status %d\n", __func__, status); | 611 | gpiod_dbg(desc, "%s: status %d\n", __func__, status); |
@@ -711,6 +729,7 @@ void gpiod_unexport(struct gpio_desc *desc) | |||
711 | dev = class_find_device(&gpio_class, NULL, desc, match_export); | 729 | dev = class_find_device(&gpio_class, NULL, desc, match_export); |
712 | if (dev) { | 730 | if (dev) { |
713 | gpio_setup_irq(desc, dev, 0); | 731 | gpio_setup_irq(desc, dev, 0); |
732 | clear_bit(FLAG_SYSFS_DIR, &desc->flags); | ||
714 | clear_bit(FLAG_EXPORT, &desc->flags); | 733 | clear_bit(FLAG_EXPORT, &desc->flags); |
715 | } else | 734 | } else |
716 | status = -ENODEV; | 735 | status = -ENODEV; |
@@ -719,8 +738,6 @@ void gpiod_unexport(struct gpio_desc *desc) | |||
719 | mutex_unlock(&sysfs_lock); | 738 | mutex_unlock(&sysfs_lock); |
720 | 739 | ||
721 | if (dev) { | 740 | if (dev) { |
722 | device_remove_file(dev, &dev_attr_edge); | ||
723 | device_remove_file(dev, &dev_attr_direction); | ||
724 | device_unregister(dev); | 741 | device_unregister(dev); |
725 | put_device(dev); | 742 | put_device(dev); |
726 | } | 743 | } |