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 | |
| 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')
| -rw-r--r-- | drivers/gpio/gpiolib-sysfs.c | 61 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib.h | 1 |
2 files changed, 40 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 | } |
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index e3a52113a541..550a5eafbd38 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h | |||
| @@ -77,6 +77,7 @@ struct gpio_desc { | |||
| 77 | #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ | 77 | #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ |
| 78 | #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ | 78 | #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ |
| 79 | #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ | 79 | #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ |
| 80 | #define FLAG_SYSFS_DIR 10 /* show sysfs direction attribute */ | ||
| 80 | 81 | ||
| 81 | #define ID_SHIFT 16 /* add new flags before this one */ | 82 | #define ID_SHIFT 16 /* add new flags before this one */ |
| 82 | 83 | ||
