Skip to contentSkip to conversation

gpiolib: Refactor gpio_export

RFC PATCH Superseded Ryan Mallon 3 participants 4 messages Oct 16, 2012 – Oct 19, 2012 GPIO

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 gpiolib: Refactor gpio_export

The gpio_export function uses nested if statements and the status variable to handle the failure cases. This makes the function logic difficult to follow. Refactor the code to abort immediately on failure using goto. This makes the code slightly longer, but significantly reduces the nesting and number of split lines and makes the code easier to read.

Signed-off-by Ryan Mallon

---

drivers/gpio/gpiolib.c +46 -40
index 5d6c71e..a142246 100644
@@ -702,68 +702,74 @@ int gpio_export(unsigned gpio, bool direction_may_change)
 {
 	unsigned long		flags;
 	struct gpio_desc	*desc;
-	int			status = -EINVAL;
+	int			status;
 	const char		*ioname = NULL;
+	struct device		*dev;
 
 	/* can't export until sysfs is available ... */
 	if (!gpio_class.p) {
 		pr_debug("%s: called too early!\n", __func__);
-		return -ENOENT;
+		status = -ENOENT;
+		goto fail;
 	}
 
-	if (!gpio_is_valid(gpio))
-		goto done;
+	if (!gpio_is_valid(gpio)) {
+		status = -EINVAL;
+		goto fail;
+	}
 
 	mutex_lock(&sysfs_lock);
 
 	spin_lock_irqsave(&gpio_lock, flags);
 	desc = &gpio_desc[gpio];
-	if (test_bit(FLAG_REQUESTED, &desc->flags)
-			&& !test_bit(FLAG_EXPORT, &desc->flags)) {
-		status = 0;
-		if (!desc->chip->direction_input
-				|| !desc->chip->direction_output)
-			direction_may_change = false;
+	if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
+	     test_bit(FLAG_EXPORT, &desc->flags)) {
+		spin_unlock_irqrestore(&gpio_lock, flags);
+		status = -ENOENT;
+		goto fail;
 	}
+
+	if (!desc->chip->direction_input || !desc->chip->direction_output)
+		direction_may_change = false;
 	spin_unlock_irqrestore(&gpio_lock, flags);
 
 	if (desc->chip->names && desc->chip->names[gpio - desc->chip->base])
 		ioname = desc->chip->names[gpio - desc->chip->base];
 
-	if (status == 0) {
-		struct device	*dev;
-
-		dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
-				desc, ioname ? ioname : "gpio%u", gpio);
-		if (!IS_ERR(dev)) {
-			status = sysfs_create_group(&dev->kobj,
-						&gpio_attr_group);
-
-			if (!status && direction_may_change)
-				status = device_create_file(dev,
-						&dev_attr_direction);
-
-			if (!status && gpio_to_irq(gpio) >= 0
-					&& (direction_may_change
-						|| !test_bit(FLAG_IS_OUT,
-							&desc->flags)))
-				status = device_create_file(dev,
-						&dev_attr_edge);
-
-			if (status != 0)
-				device_unregister(dev);
-		} else
-			status = PTR_ERR(dev);
-		if (status == 0)
-			set_bit(FLAG_EXPORT, &desc->flags);
+	dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
+			    desc, ioname ? ioname : "gpio%u", gpio);
+	if (IS_ERR(dev)) {
+		status = PTR_ERR(dev);
+		goto fail_unlock;
 	}
 
-	mutex_unlock(&sysfs_lock);
-
-done:
+	status = sysfs_create_group(&dev->kobj, &gpio_attr_group);
 	if (status)
-		pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
+		goto fail_unregister_device;
+
+	if (direction_may_change) {
+		status = device_create_file(dev, &dev_attr_direction);
+		if (status)
+			goto fail_unregister_device;
+	}
 
+	if (gpio_to_irq(gpio) >= 0 && (direction_may_change ||
+				       !test_bit(FLAG_IS_OUT, &desc->flags))) {
+		status = device_create_file(dev, &dev_attr_edge);
+		if (status)
+			goto fail_unregister_device;
+	}
+
+	set_bit(FLAG_EXPORT, &desc->flags);
+	mutex_unlock(&sysfs_lock);
+	return 0;
+
+fail_unregister_device:
+	device_unregister(dev);
+fail_unlock:
+	mutex_unlock(&sysfs_lock);
+fail:
+	pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
 	return status;
 }
 EXPORT_SYMBOL_GPL(gpio_export);
#1 lore raw
Quoted 8 lines · Ryan Mallon

The gpio_export function uses nested if statements and the status variable to handle the failure cases. This makes the function logic difficult to follow. Refactor the code to abort immediately on failure using goto. This makes the code slightly longer, but significantly reduces the nesting and number of split lines and makes the code easier to read.

Signed-off-by Ryan Mallon

Very good initiative!

Quoted 6 lines

+++ b/drivers/gpio/gpiolib.c

drivers/gpio/gpiolib.c +0 -1
@@ -702,68 +702,74 @@ int gpio_export(unsigned gpio, bool direction_may_change)
 {
        unsigned long           flags;
        struct gpio_desc        *desc;
-       int                     status = -EINVAL;

drivers/gpio/gpiolib.c int gpio_export(unsigned gpio, bool direction_may_change) · line 713

Quoted 10 lines
drivers/gpio/gpiolib.c +4 -1
+       int                     status;
        const char              *ioname = NULL;
+       struct device           *dev;
 
        /* can't export until sysfs is available ... */
        if (!gpio_class.p) {
                pr_debug("%s: called too early!\n", __func__);
-               return -ENOENT;
+               status = -ENOENT;
+               goto fail;

Why bother with all the goto:s here since there are no resources to clean up? Just pr_debug() and return -ENOENT; is good enough.

I don't quite see the point.

Arguably this should be pr_err() or something BTW, just debug() may hide serious bugs.

drivers/gpio/gpiolib.c int gpio_export(unsigned gpio, bool direction_may_change) · line 719

Quoted 6 lines
drivers/gpio/gpiolib.c +4 -2
-       if (!gpio_is_valid(gpio))
-               goto done;
+       if (!gpio_is_valid(gpio)) {
+               status = -EINVAL;
+               goto fail;
+       }

Why not just pr_err(..); return -EINVAL; ?

drivers/gpio/gpiolib.c int gpio_export(unsigned gpio, bool direction_may_change) · line 729

Quoted 11 lines
drivers/gpio/gpiolib.c +5 -6
-       if (test_bit(FLAG_REQUESTED, &desc->flags)
-                       && !test_bit(FLAG_EXPORT, &desc->flags)) {
-               status = 0;
-               if (!desc->chip->direction_input
-                               || !desc->chip->direction_output)
-                       direction_may_change = false;
+       if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
+            test_bit(FLAG_EXPORT, &desc->flags)) {
+               spin_unlock_irqrestore(&gpio_lock, flags);
+               status = -ENOENT;
+               goto fail;

I would just print and return here as well.

Quoted 29 lines
drivers/gpio/gpiolib.c +3 -21
+
+       if (!desc->chip->direction_input || !desc->chip->direction_output)
+               direction_may_change = false;
        spin_unlock_irqrestore(&gpio_lock, flags);
 
        if (desc->chip->names && desc->chip->names[gpio - desc->chip->base])
                ioname = desc->chip->names[gpio - desc->chip->base];
 
-       if (status == 0) {
-               struct device   *dev;
-
-               dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
-                               desc, ioname ? ioname : "gpio%u", gpio);
-               if (!IS_ERR(dev)) {
-                       status = sysfs_create_group(&dev->kobj,
-                                               &gpio_attr_group);
-
-                       if (!status && direction_may_change)
-                               status = device_create_file(dev,
-                                               &dev_attr_direction);
-
-                       if (!status && gpio_to_irq(gpio) >= 0
-                                       && (direction_may_change
-                                               || !test_bit(FLAG_IS_OUT,
-                                                       &desc->flags)))
-                               status = device_create_file(dev,
-                                               &dev_attr_edge);
-
-                       if (status != 0)

drivers/gpio/gpiolib.c int gpio_export(unsigned gpio, bool direction_may_change) · line 743

Quoted 10 lines
drivers/gpio/gpiolib.c +5 -5
-                               device_unregister(dev);
-               } else
-                       status = PTR_ERR(dev);
-               if (status == 0)
-                       set_bit(FLAG_EXPORT, &desc->flags);
+       dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
+                           desc, ioname ? ioname : "gpio%u", gpio);
+       if (IS_ERR(dev)) {
+               status = PTR_ERR(dev);
+               goto fail_unlock;

Since this involves clean-up in the form of unlocking the mutex this goto is fine however.

Same for unregister_device:.

Yours,
Linus Walleij

#2 lore raw
Quoted 33 lines · Linus Walleij
Quoted 8 lines · Ryan Mallon

The gpio_export function uses nested if statements and the status variable to handle the failure cases. This makes the function logic difficult to follow. Refactor the code to abort immediately on failure using goto. This makes the code slightly longer, but significantly reduces the nesting and number of split lines and makes the code easier to read.

Signed-off-by Ryan Mallon

Very good initiative!

Quoted 6 lines

+++ b/drivers/gpio/gpiolib.c

drivers/gpio/gpiolib.c +0 -1
@@ -702,68 +702,74 @@ int gpio_export(unsigned gpio, bool direction_may_change)
 {
        unsigned long           flags;
        struct gpio_desc        *desc;
-       int                     status = -EINVAL;

drivers/gpio/gpiolib.c int gpio_export(unsigned gpio, bool direction_may_change) · line 713

Quoted 10 lines
drivers/gpio/gpiolib.c +4 -1
+       int                     status;
        const char              *ioname = NULL;
+       struct device           *dev;
 
        /* can't export until sysfs is available ... */
        if (!gpio_class.p) {
                pr_debug("%s: called too early!\n", __func__);
-               return -ENOENT;
+               status = -ENOENT;
+               goto fail;

Why bother with all the goto:s here since there are no resources to clean up? Just pr_debug() and return -ENOENT; is good enough.

Quoted 1 line

I don't quite see the point.

I did it this way just so that there would be a single exit point. I don't mind either way, so I'll update the ones without any clean up to simply return.

Quoted 2 lines

Arguably this should be pr_err() or something BTW, just debug() may hide serious bugs.

Not sure about that. User-space can check the return code if it wants to find out why the call failed. We shouldn't unconditionally spam the log with error messages on sysfs access since it makes an easy DoS vector on the syslog. The pr_debug messages I think are really for people who are developing on gpiolib.

~Ryan

#3 lore raw
Quoted 35 lines · Ryan Mallon
Quoted 33 lines · Linus Walleij
Quoted 8 lines · Ryan Mallon

The gpio_export function uses nested if statements and the status variable to handle the failure cases. This makes the function logic difficult to follow. Refactor the code to abort immediately on failure using goto. This makes the code slightly longer, but significantly reduces the nesting and number of split lines and makes the code easier to read.

Signed-off-by Ryan Mallon

Very good initiative!

Quoted 6 lines

+++ b/drivers/gpio/gpiolib.c

drivers/gpio/gpiolib.c +0 -1
@@ -702,68 +702,74 @@ int gpio_export(unsigned gpio, bool direction_may_change)
 {
        unsigned long           flags;
        struct gpio_desc        *desc;
-       int                     status = -EINVAL;

drivers/gpio/gpiolib.c int gpio_export(unsigned gpio, bool direction_may_change) · line 713

Quoted 10 lines
drivers/gpio/gpiolib.c +4 -1
+       int                     status;
        const char              *ioname = NULL;
+       struct device           *dev;
 
        /* can't export until sysfs is available ... */
        if (!gpio_class.p) {
                pr_debug("%s: called too early!\n", __func__);
-               return -ENOENT;
+               status = -ENOENT;
+               goto fail;

Why bother with all the goto:s here since there are no resources to clean up? Just pr_debug() and return -ENOENT; is good enough.

Quoted 5 lines
Quoted 1 line

I don't quite see the point.

I did it this way just so that there would be a single exit point. I don't mind either way, so I'll update the ones without any clean up to simply return.

Single exit points are overrated. The code is shorter and easier to read to return immediately if there isn't any unwinding to do.

g.