diff options
author | Johan Hovold <johan@kernel.org> | 2015-01-13 07:00:05 -0500 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2015-01-15 11:20:15 -0500 |
commit | 0915e6feb38de8d3601819992a5bd050201a56fa (patch) | |
tree | 246c1724e7a827583fa70147869b4bcbb15b883c | |
parent | 121b6a79955a3a3fd7bbb9b8cb88d5b9dad6283d (diff) |
gpio: sysfs: fix gpio device-attribute leak
The gpio device attributes were never destroyed when the gpio was
unexported (or on export failures).
Use device_create_with_groups() to create the default device attributes
of the gpio class device. Note that this also fixes the
attribute-creation race with userspace for these attributes.
Remove contingent attributes in export error path and on unexport.
Fixes: d8f388d8dc8d ("gpio: sysfs interface")
Cc: stable <stable@vger.kernel.org> # v2.6.27+
Signed-off-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r-- | drivers/gpio/gpiolib-sysfs.c | 28 |
1 files changed, 13 insertions, 15 deletions
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 33cf4bd0ed2d..fd4d9423f6e6 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c | |||
@@ -128,7 +128,7 @@ static ssize_t gpio_value_store(struct device *dev, | |||
128 | return status; | 128 | return status; |
129 | } | 129 | } |
130 | 130 | ||
131 | static const DEVICE_ATTR(value, 0644, | 131 | static DEVICE_ATTR(value, 0644, |
132 | gpio_value_show, gpio_value_store); | 132 | gpio_value_show, gpio_value_store); |
133 | 133 | ||
134 | static irqreturn_t gpio_sysfs_irq(int irq, void *priv) | 134 | static irqreturn_t gpio_sysfs_irq(int irq, void *priv) |
@@ -353,18 +353,15 @@ static ssize_t gpio_active_low_store(struct device *dev, | |||
353 | return status ? : size; | 353 | return status ? : size; |
354 | } | 354 | } |
355 | 355 | ||
356 | static const 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 const struct attribute *gpio_attrs[] = { | 359 | static struct attribute *gpio_attrs[] = { |
360 | &dev_attr_value.attr, | 360 | &dev_attr_value.attr, |
361 | &dev_attr_active_low.attr, | 361 | &dev_attr_active_low.attr, |
362 | NULL, | 362 | NULL, |
363 | }; | 363 | }; |
364 | 364 | ATTRIBUTE_GROUPS(gpio); | |
365 | static const struct attribute_group gpio_attr_group = { | ||
366 | .attrs = (struct attribute **) gpio_attrs, | ||
367 | }; | ||
368 | 365 | ||
369 | /* | 366 | /* |
370 | * /sys/class/gpio/gpiochipN/ | 367 | * /sys/class/gpio/gpiochipN/ |
@@ -561,18 +558,15 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) | |||
561 | if (desc->chip->names && desc->chip->names[offset]) | 558 | if (desc->chip->names && desc->chip->names[offset]) |
562 | ioname = desc->chip->names[offset]; | 559 | ioname = desc->chip->names[offset]; |
563 | 560 | ||
564 | dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), | 561 | dev = device_create_with_groups(&gpio_class, desc->chip->dev, |
565 | desc, ioname ? ioname : "gpio%u", | 562 | MKDEV(0, 0), desc, gpio_groups, |
566 | desc_to_gpio(desc)); | 563 | ioname ? ioname : "gpio%u", |
564 | desc_to_gpio(desc)); | ||
567 | if (IS_ERR(dev)) { | 565 | if (IS_ERR(dev)) { |
568 | status = PTR_ERR(dev); | 566 | status = PTR_ERR(dev); |
569 | goto fail_unlock; | 567 | goto fail_unlock; |
570 | } | 568 | } |
571 | 569 | ||
572 | status = sysfs_create_group(&dev->kobj, &gpio_attr_group); | ||
573 | if (status) | ||
574 | goto fail_unregister_device; | ||
575 | |||
576 | if (direction_may_change) { | 570 | if (direction_may_change) { |
577 | status = device_create_file(dev, &dev_attr_direction); | 571 | status = device_create_file(dev, &dev_attr_direction); |
578 | if (status) | 572 | if (status) |
@@ -583,13 +577,15 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) | |||
583 | !test_bit(FLAG_IS_OUT, &desc->flags))) { | 577 | !test_bit(FLAG_IS_OUT, &desc->flags))) { |
584 | status = device_create_file(dev, &dev_attr_edge); | 578 | status = device_create_file(dev, &dev_attr_edge); |
585 | if (status) | 579 | if (status) |
586 | goto fail_unregister_device; | 580 | goto fail_remove_attr_direction; |
587 | } | 581 | } |
588 | 582 | ||
589 | set_bit(FLAG_EXPORT, &desc->flags); | 583 | set_bit(FLAG_EXPORT, &desc->flags); |
590 | mutex_unlock(&sysfs_lock); | 584 | mutex_unlock(&sysfs_lock); |
591 | return 0; | 585 | return 0; |
592 | 586 | ||
587 | fail_remove_attr_direction: | ||
588 | device_remove_file(dev, &dev_attr_direction); | ||
593 | fail_unregister_device: | 589 | fail_unregister_device: |
594 | device_unregister(dev); | 590 | device_unregister(dev); |
595 | fail_unlock: | 591 | fail_unlock: |
@@ -723,6 +719,8 @@ void gpiod_unexport(struct gpio_desc *desc) | |||
723 | mutex_unlock(&sysfs_lock); | 719 | mutex_unlock(&sysfs_lock); |
724 | 720 | ||
725 | if (dev) { | 721 | if (dev) { |
722 | device_remove_file(dev, &dev_attr_edge); | ||
723 | device_remove_file(dev, &dev_attr_direction); | ||
726 | device_unregister(dev); | 724 | device_unregister(dev); |
727 | put_device(dev); | 725 | put_device(dev); |
728 | } | 726 | } |