* [PATCH] gpio: acpi: modernize resource management using cleanup.h
@ 2026-05-06 9:29 Marco Scardovi
2026-05-06 11:32 ` Mika Westerberg
0 siblings, 1 reply; 3+ messages in thread
From: Marco Scardovi @ 2026-05-06 9:29 UTC (permalink / raw)
To: mika.westerberg, mathias.nyman; +Cc: linux-gpio, linux-acpi, linux-kernel
Hi everyone,
I was looking for ways to switch to modern cleanup guards and auto-freeing
pointers to simplify error paths and synchronization in gpiolib-acpi-core.c
so I came up with the patch you can find below.
Here you can see the main points I've worked on:
- Use DEFINE_FREE() for gpio_desc and ACPI resources.
- Use guard(mutex)() within the OpRegion handler loop for automatic locking.
- Use __free() for automatic descriptor and memory cleanup.
- Fix off-by-one error in GPIO pin bounds check.
- Return AE_OK on out-of-range pins to allow processing other resources
even if one is misconfigured in firmware.
- Use break instead of goto in OpRegion handler for cleaner control flow
leveraging auto-cleanup.
I've tested it (both build and functionality) against linux-next-20260430.
Signed-off-by: Marco Scardovi <mscardovi95@gmail.com>
---
drivers/gpio/gpiolib-acpi-core.c | 94
+++++++++++++++++++---------------------
1 file changed, 43 insertions(+), 51 deletions(-)
diff --git a/drivers/gpio/gpiolib-acpi-core.c
b/drivers/gpio/gpiolib-acpi-core.c
index eb8a40cfb7a9..19a18222b7b2 100644
--- a/drivers/gpio/gpiolib-acpi-core.c
+++ b/drivers/gpio/gpiolib-acpi-core.c
@@ -7,6 +7,9 @@
* Mika Westerberg <mika.westerberg@linux.intel.com>
*/
+#include <linux/cleanup.h>
+#include <linux/slab.h>
+
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/errno.h>
@@ -23,6 +26,16 @@
#include "gpiolib.h"
#include "gpiolib-acpi.h"
+DEFINE_FREE(free_gpio_desc, struct gpio_desc *, {
+ if (_T)
+ gpiochip_free_own_desc(_T);
+})
+
+DEFINE_FREE(acpi_free, void *, {
+ if (_T)
+ ACPI_FREE(_T);
+})
+
/**
* struct acpi_gpio_event - ACPI GPIO event handler data
*
@@ -361,6 +374,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct
acpi_resource *ares,
struct acpi_gpio_event *event;
irq_handler_t handler = NULL;
struct gpio_desc *desc;
+ struct gpio_desc *desc_guard __free(free_gpio_desc) = NULL;
unsigned int pin;
int ret, irq;
@@ -370,6 +384,11 @@ static acpi_status acpi_gpiochip_alloc_event(struct
acpi_resource *ares,
handle = ACPI_HANDLE(chip->parent);
pin = agpio->pin_table[0];
+ if (pin >= chip->ngpio) {
+ dev_err(chip->parent, "Failed to request GPIO for pin 0x%04X,
out of range\n", pin);
+ return AE_OK;
+ }
+
if (pin <= 255) {
char ev_name[8];
sprintf(ev_name, "_%c%02X",
@@ -392,31 +411,26 @@ static acpi_status
acpi_gpiochip_alloc_event(struct acpi_resource *ares,
desc = acpi_request_own_gpiod(chip, agpio, 0, "ACPI:Event");
if (IS_ERR(desc)) {
- dev_err(chip->parent,
- "Failed to request GPIO for pin 0x%04X, err %pe\n",
- pin, desc);
+ dev_err(chip->parent, "Failed to request GPIO for pin 0x%04X,
err %pe\n", pin, desc);
return AE_OK;
}
+ desc_guard = desc;
ret = gpiochip_lock_as_irq(chip, pin);
if (ret) {
- dev_err(chip->parent,
- "Failed to lock GPIO pin 0x%04X as interrupt, err %d\n",
- pin, ret);
- goto fail_free_desc;
+ dev_err(chip->parent, "Failed to lock GPIO pin 0x%04X as
interrupt, err %d\n", pin, ret);
+ return AE_OK;
}
irq = gpiod_to_irq(desc);
if (irq < 0) {
- dev_err(chip->parent,
- "Failed to translate GPIO pin 0x%04X to IRQ, err %d\n",
- pin, irq);
- goto fail_unlock_irq;
+ dev_err(chip->parent, "Failed to translate GPIO pin 0x%04X to
IRQ, err %d\n", pin, irq);
+ goto err_unlock;
}
event = kzalloc_obj(*event);
if (!event)
- goto fail_unlock_irq;
+ goto err_unlock;
event->irqflags = IRQF_ONESHOT;
if (agpio->triggering == ACPI_LEVEL_SENSITIVE) {
@@ -444,17 +458,15 @@ static acpi_status
acpi_gpiochip_alloc_event(struct acpi_resource *ares,
event->irq = irq;
event->irq_is_wake = acpi_gpio_irq_is_wake(chip->parent, agpio);
event->pin = pin;
- event->desc = desc;
+ /* Transfer ownership to event, prevent auto-free */
+ event->desc = no_free_ptr(desc_guard);
list_add_tail(&event->node, &acpi_gpio->events);
return AE_OK;
-fail_unlock_irq:
+err_unlock:
gpiochip_unlock_as_irq(chip, pin);
-fail_free_desc:
- gpiochip_free_own_desc(desc);
-
return AE_OK;
}
@@ -1086,7 +1098,7 @@ acpi_gpio_adr_space_handler(u32 function,
acpi_physical_address address,
struct acpi_gpio_chip *achip = region_context;
struct gpio_chip *chip = achip->chip;
struct acpi_resource_gpio *agpio;
- struct acpi_resource *ares;
+ struct acpi_resource *ares __free(acpi_free) = NULL;
u16 pin_index = address;
acpi_status status;
int length;
@@ -1097,20 +1109,17 @@ acpi_gpio_adr_space_handler(u32 function,
acpi_physical_address address,
if (ACPI_FAILURE(status))
return status;
- if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO)) {
- ACPI_FREE(ares);
+ if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO))
return AE_BAD_PARAMETER;
- }
agpio = &ares->data.gpio;
if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT &&
- function == ACPI_WRITE)) {
- ACPI_FREE(ares);
+ function == ACPI_WRITE))
return AE_BAD_PARAMETER;
- }
length = min(agpio->pin_table_length, pin_index + bits);
+ status = AE_OK;
for (i = pin_index; i < length; ++i) {
unsigned int pin = agpio->pin_table[i];
struct acpi_gpio_connection *conn;
@@ -1118,7 +1127,7 @@ acpi_gpio_adr_space_handler(u32 function,
acpi_physical_address address,
u16 word, shift;
bool found;
- mutex_lock(&achip->conn_lock);
+ guard(mutex)(&achip->conn_lock);
found = false;
list_for_each_entry(conn, &achip->conns, node) {
@@ -1150,17 +1159,15 @@ acpi_gpio_adr_space_handler(u32 function,
acpi_physical_address address,
if (!found) {
desc = acpi_request_own_gpiod(chip, agpio, i,
"ACPI:OpRegion");
if (IS_ERR(desc)) {
- mutex_unlock(&achip->conn_lock);
status = AE_ERROR;
- goto out;
+ break;
}
conn = kzalloc_obj(*conn);
if (!conn) {
gpiochip_free_own_desc(desc);
- mutex_unlock(&achip->conn_lock);
status = AE_NO_MEMORY;
- goto out;
+ break;
}
conn->pin = pin;
@@ -1168,8 +1175,6 @@ acpi_gpio_adr_space_handler(u32 function,
acpi_physical_address address,
list_add_tail(&conn->node, &achip->conns);
}
- mutex_unlock(&achip->conn_lock);
-
/*
* For the cases when OperationRegion() consists of more than
* 64 bits calculate the word and bit shift to use that one to
@@ -1188,8 +1193,6 @@ acpi_gpio_adr_space_handler(u32 function,
acpi_physical_address address,
}
}
-out:
- ACPI_FREE(ares);
return status;
}
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] gpio: acpi: modernize resource management using cleanup.h
2026-05-06 9:29 [PATCH] gpio: acpi: modernize resource management using cleanup.h Marco Scardovi
@ 2026-05-06 11:32 ` Mika Westerberg
2026-05-06 12:46 ` Andy Shevchenko
0 siblings, 1 reply; 3+ messages in thread
From: Mika Westerberg @ 2026-05-06 11:32 UTC (permalink / raw)
To: Marco Scardovi
Cc: mathias.nyman, linux-gpio, linux-acpi, linux-kernel,
Andy Shevchenko
Hi.
+Andy
On Wed, May 06, 2026 at 11:29:36AM +0200, Marco Scardovi wrote:
> Hi everyone,
>
> I was looking for ways to switch to modern cleanup guards and auto-freeing
> pointers to simplify error paths and synchronization in gpiolib-acpi-core.c
> so I came up with the patch you can find below.
>
> Here you can see the main points I've worked on:
> - Use DEFINE_FREE() for gpio_desc and ACPI resources.
> - Use guard(mutex)() within the OpRegion handler loop for automatic locking.
> - Use __free() for automatic descriptor and memory cleanup.
> - Fix off-by-one error in GPIO pin bounds check.
> - Return AE_OK on out-of-range pins to allow processing other resources
> even if one is misconfigured in firmware.
> - Use break instead of goto in OpRegion handler for cleaner control flow
> leveraging auto-cleanup.
That should be several patches not one doing all these.
> I've tested it (both build and functionality) against linux-next-20260430.
>
> Signed-off-by: Marco Scardovi <mscardovi95@gmail.com>
> ---
> drivers/gpio/gpiolib-acpi-core.c | 94
> +++++++++++++++++++---------------------
> 1 file changed, 43 insertions(+), 51 deletions(-)
>
> diff --git a/drivers/gpio/gpiolib-acpi-core.c
> b/drivers/gpio/gpiolib-acpi-core.c
> index eb8a40cfb7a9..19a18222b7b2 100644
> --- a/drivers/gpio/gpiolib-acpi-core.c
> +++ b/drivers/gpio/gpiolib-acpi-core.c
> @@ -7,6 +7,9 @@
> * Mika Westerberg <mika.westerberg@linux.intel.com>
> */
>
> +#include <linux/cleanup.h>
> +#include <linux/slab.h>
> +
Drop the empty line.
> #include <linux/acpi.h>
> #include <linux/dmi.h>
> #include <linux/errno.h>
> @@ -23,6 +26,16 @@
> #include "gpiolib.h"
> #include "gpiolib-acpi.h"
>
> +DEFINE_FREE(free_gpio_desc, struct gpio_desc *, {
> + if (_T)
> + gpiochip_free_own_desc(_T);
> +})
> +
> +DEFINE_FREE(acpi_free, void *, {
> + if (_T)
> + ACPI_FREE(_T);
> +})
These are white space damaged. Also I'm not big fan of these but if Andy is
fine then works for me too. However, please test with KASAN and kmemleak
enabled that you don't break anything.
> +
> /**
> * struct acpi_gpio_event - ACPI GPIO event handler data
> *
> @@ -361,6 +374,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct
> acpi_resource *ares,
> struct acpi_gpio_event *event;
> irq_handler_t handler = NULL;
> struct gpio_desc *desc;
> + struct gpio_desc *desc_guard __free(free_gpio_desc) = NULL;
> unsigned int pin;
> int ret, irq;
>
> @@ -370,6 +384,11 @@ static acpi_status acpi_gpiochip_alloc_event(struct
> acpi_resource *ares,
> handle = ACPI_HANDLE(chip->parent);
> pin = agpio->pin_table[0];
>
> + if (pin >= chip->ngpio) {
> + dev_err(chip->parent, "Failed to request GPIO for pin 0x%04X, out
> of range\n", pin);
This is damaged too.
Actually let's start with a proper patch and then look the details :)
> + return AE_OK;
> + }
> +
> if (pin <= 255) {
> char ev_name[8];
> sprintf(ev_name, "_%c%02X",
> @@ -392,31 +411,26 @@ static acpi_status acpi_gpiochip_alloc_event(struct
> acpi_resource *ares,
>
> desc = acpi_request_own_gpiod(chip, agpio, 0, "ACPI:Event");
> if (IS_ERR(desc)) {
> - dev_err(chip->parent,
> - "Failed to request GPIO for pin 0x%04X, err %pe\n",
> - pin, desc);
> + dev_err(chip->parent, "Failed to request GPIO for pin 0x%04X, err
> %pe\n", pin, desc);
> return AE_OK;
> }
> + desc_guard = desc;
>
> ret = gpiochip_lock_as_irq(chip, pin);
> if (ret) {
> - dev_err(chip->parent,
> - "Failed to lock GPIO pin 0x%04X as interrupt, err %d\n",
> - pin, ret);
> - goto fail_free_desc;
> + dev_err(chip->parent, "Failed to lock GPIO pin 0x%04X as interrupt,
> err %d\n", pin, ret);
> + return AE_OK;
> }
>
> irq = gpiod_to_irq(desc);
> if (irq < 0) {
> - dev_err(chip->parent,
> - "Failed to translate GPIO pin 0x%04X to IRQ, err %d\n",
> - pin, irq);
> - goto fail_unlock_irq;
> + dev_err(chip->parent, "Failed to translate GPIO pin 0x%04X to IRQ,
> err %d\n", pin, irq);
> + goto err_unlock;
> }
>
> event = kzalloc_obj(*event);
> if (!event)
> - goto fail_unlock_irq;
> + goto err_unlock;
>
> event->irqflags = IRQF_ONESHOT;
> if (agpio->triggering == ACPI_LEVEL_SENSITIVE) {
> @@ -444,17 +458,15 @@ static acpi_status acpi_gpiochip_alloc_event(struct
> acpi_resource *ares,
> event->irq = irq;
> event->irq_is_wake = acpi_gpio_irq_is_wake(chip->parent, agpio);
> event->pin = pin;
> - event->desc = desc;
> + /* Transfer ownership to event, prevent auto-free */
> + event->desc = no_free_ptr(desc_guard);
>
> list_add_tail(&event->node, &acpi_gpio->events);
>
> return AE_OK;
>
> -fail_unlock_irq:
> +err_unlock:
> gpiochip_unlock_as_irq(chip, pin);
> -fail_free_desc:
> - gpiochip_free_own_desc(desc);
> -
> return AE_OK;
> }
>
> @@ -1086,7 +1098,7 @@ acpi_gpio_adr_space_handler(u32 function,
> acpi_physical_address address,
> struct acpi_gpio_chip *achip = region_context;
> struct gpio_chip *chip = achip->chip;
> struct acpi_resource_gpio *agpio;
> - struct acpi_resource *ares;
> + struct acpi_resource *ares __free(acpi_free) = NULL;
> u16 pin_index = address;
> acpi_status status;
> int length;
> @@ -1097,20 +1109,17 @@ acpi_gpio_adr_space_handler(u32 function,
> acpi_physical_address address,
> if (ACPI_FAILURE(status))
> return status;
>
> - if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO)) {
> - ACPI_FREE(ares);
> + if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO))
> return AE_BAD_PARAMETER;
> - }
>
> agpio = &ares->data.gpio;
>
> if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT &&
> - function == ACPI_WRITE)) {
> - ACPI_FREE(ares);
> + function == ACPI_WRITE))
> return AE_BAD_PARAMETER;
> - }
>
> length = min(agpio->pin_table_length, pin_index + bits);
> + status = AE_OK;
> for (i = pin_index; i < length; ++i) {
> unsigned int pin = agpio->pin_table[i];
> struct acpi_gpio_connection *conn;
> @@ -1118,7 +1127,7 @@ acpi_gpio_adr_space_handler(u32 function,
> acpi_physical_address address,
> u16 word, shift;
> bool found;
>
> - mutex_lock(&achip->conn_lock);
> + guard(mutex)(&achip->conn_lock);
>
> found = false;
> list_for_each_entry(conn, &achip->conns, node) {
> @@ -1150,17 +1159,15 @@ acpi_gpio_adr_space_handler(u32 function,
> acpi_physical_address address,
> if (!found) {
> desc = acpi_request_own_gpiod(chip, agpio, i, "ACPI:OpRegion");
> if (IS_ERR(desc)) {
> - mutex_unlock(&achip->conn_lock);
> status = AE_ERROR;
> - goto out;
> + break;
> }
>
> conn = kzalloc_obj(*conn);
> if (!conn) {
> gpiochip_free_own_desc(desc);
> - mutex_unlock(&achip->conn_lock);
> status = AE_NO_MEMORY;
> - goto out;
> + break;
> }
>
> conn->pin = pin;
> @@ -1168,8 +1175,6 @@ acpi_gpio_adr_space_handler(u32 function,
> acpi_physical_address address,
> list_add_tail(&conn->node, &achip->conns);
> }
>
> - mutex_unlock(&achip->conn_lock);
> -
> /*
> * For the cases when OperationRegion() consists of more than
> * 64 bits calculate the word and bit shift to use that one to
> @@ -1188,8 +1193,6 @@ acpi_gpio_adr_space_handler(u32 function,
> acpi_physical_address address,
> }
> }
>
> -out:
> - ACPI_FREE(ares);
> return status;
> }
>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] gpio: acpi: modernize resource management using cleanup.h
2026-05-06 11:32 ` Mika Westerberg
@ 2026-05-06 12:46 ` Andy Shevchenko
0 siblings, 0 replies; 3+ messages in thread
From: Andy Shevchenko @ 2026-05-06 12:46 UTC (permalink / raw)
To: Mika Westerberg
Cc: Marco Scardovi, mathias.nyman, linux-gpio, linux-acpi,
linux-kernel
On Wed, May 06, 2026 at 01:32:15PM +0200, Mika Westerberg wrote:
> +Andy
Thanks for Cc'ing me. Marco, you have to follow the MAINTAINERS database when
submitting this. For simplicity you may use my "smart" script [1] that does
almost right (it still uses heuristics but nobody complained so far) the Cc
lists.
> On Wed, May 06, 2026 at 11:29:36AM +0200, Marco Scardovi wrote:
> >
> > I was looking for ways to switch to modern cleanup guards and auto-freeing
> > pointers to simplify error paths and synchronization in gpiolib-acpi-core.c
> > so I came up with the patch you can find below.
> >
> > Here you can see the main points I've worked on:
> > - Use DEFINE_FREE() for gpio_desc and ACPI resources.
> > - Use guard(mutex)() within the OpRegion handler loop for automatic locking.
> > - Use __free() for automatic descriptor and memory cleanup.
> > - Fix off-by-one error in GPIO pin bounds check.
> > - Return AE_OK on out-of-range pins to allow processing other resources
> > even if one is misconfigured in firmware.
> > - Use break instead of goto in OpRegion handler for cleaner control flow
> > leveraging auto-cleanup.
>
> That should be several patches not one doing all these.
As Mika said, please, split this to the per-logical change series.
> > I've tested it (both build and functionality) against linux-next-20260430.
This is assumed, but you can put that into the cover letter.
> > Signed-off-by: Marco Scardovi <mscardovi95@gmail.com>
...
> > +#include <linux/cleanup.h>
> > +#include <linux/slab.h>
> > +
>
> Drop the empty line.
And follow the existing order.
...
> > +DEFINE_FREE(free_gpio_desc, struct gpio_desc *, {
> > + if (_T)
> > + gpiochip_free_own_desc(_T);
> > +})
> > +
> > +DEFINE_FREE(acpi_free, void *, {
> > + if (_T)
> > + ACPI_FREE(_T);
> > +})
> These are white space damaged. Also I'm not big fan of these but if Andy is
> fine then works for me too. However, please test with KASAN and kmemleak
> enabled that you don't break anything.
They are fine, but:
- they need to be just one liner as it's done elsewhere
- the {} are redundant (see existing examples)
- they need to be added per subsystem to their headers
(so, two more patches on top of whatever this one has to be split to for
different subsystems).
...
> > struct acpi_gpio_event *event;
> > irq_handler_t handler = NULL;
> > struct gpio_desc *desc;
> > + struct gpio_desc *desc_guard __free(free_gpio_desc) = NULL;
This way of defining is highly discouraged. See below.
> > desc = acpi_request_own_gpiod(chip, agpio, 0, "ACPI:Event");
This one should be used otherwise as
struct gpio_desc *desc __free(free_gpio_desc) =
acpi_request_own_gpiod(chip, agpio, 0, "ACPI:Event");
...
> > - struct acpi_resource *ares;
> > + struct acpi_resource *ares __free(acpi_free) = NULL;
As per above.
...
[1]: https://github.com/andy-shev/home-bin-tools/blob/master/ge2maintainer.sh
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-06 12:46 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-06 9:29 [PATCH] gpio: acpi: modernize resource management using cleanup.h Marco Scardovi
2026-05-06 11:32 ` Mika Westerberg
2026-05-06 12:46 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox