The underlying chip can be removed asynchronously. `gdev->srcu` is used to ensure the synchronization before accessing `gdev->chip`.
Revocable encapsulates the details in a neat way. Add revocable provider handle for the corresponding struct gpio_chip in struct gpio_device so that we can start to hide the synchronization details. Signed-off-by: Tzung-Bi Shih <[email protected]> --- drivers/gpio/gpiolib.c | 19 +++++++++++++++++++ drivers/gpio/gpiolib.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index efe72b81e131..6226dc738281 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -22,6 +22,7 @@ #include <linux/nospec.h> #include <linux/of.h> #include <linux/pinctrl/consumer.h> +#include <linux/revocable.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/srcu.h> @@ -1105,6 +1106,12 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) goto err_cleanup_gdev_srcu; + gdev->chip_rp = revocable_provider_alloc(gc); + if (!gdev->chip_rp) { + ret = -ENOMEM; + goto err_cleanup_desc_srcu; + } + device_initialize(&gdev->dev); /* From this point, the .release() function cleans up gdev->dev */ gdev->dev.type = &gpio_dev_type; @@ -1258,9 +1265,20 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, list_del_rcu(&gdev->list); synchronize_srcu(&gpio_devices_srcu); err_put_device: + /* + * Error handling of the revocable provider is tricky. Unlike other + * allocated resources for `gdev` are freed in gpiodev_release(). + * We need to call revocable_provider_revoke() here as it's designed + * to be called when the chip is gone (i.e. gpiochip_remove()). + * + * Note: must before gpio_device_put() as it frees `gdev`. + */ + revocable_provider_revoke(gdev->chip_rp); gpio_device_put(gdev); goto err_print_message; +err_cleanup_desc_srcu: + cleanup_srcu_struct(&gdev->desc_srcu); err_cleanup_gdev_srcu: cleanup_srcu_struct(&gdev->srcu); err_free_label: @@ -1307,6 +1325,7 @@ void gpiochip_remove(struct gpio_chip *gc) /* Numb the device, cancelling all outstanding operations */ rcu_assign_pointer(gdev->chip, NULL); synchronize_srcu(&gdev->srcu); + revocable_provider_revoke(gdev->chip_rp); gpio_device_teardown_shared(gdev); gpiochip_irqchip_remove(gc); acpi_gpiochip_remove(gc); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 77f6f2936dc2..e61db3a75e84 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -52,6 +52,7 @@ * @device_notifier: used to notify character device wait queues about the GPIO * device being unregistered * @srcu: protects the pointer to the underlying GPIO chip + * @chip_rp: revocable provider handle for the corresponding struct gpio_chip. * @pin_ranges: range of pins served by the GPIO driver * * This state container holds most of the runtime variable data @@ -79,6 +80,7 @@ struct gpio_device { struct workqueue_struct *line_state_wq; struct blocking_notifier_head device_notifier; struct srcu_struct srcu; + struct revocable_provider *chip_rp; #ifdef CONFIG_PINCTRL /* -- 2.52.0.457.g6b5491de43-goog
