lkml.org 
[lkml]   [2023]   [Sep]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v3 03/11] gpiolib: provide gpio_device_find()
    Date
    From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

    gpiochip_find() is wrong and its kernel doc is misleading as the
    function doesn't return a reference to the gpio_chip but just a raw
    pointer. The chip itself is not guaranteed to stay alive, in fact it can
    be deleted at any point. Also: other than GPIO drivers themselves,
    nobody else has any business accessing gpio_chip structs.

    Provide a new gpio_device_find() function that returns a real reference
    to the opaque gpio_device structure that is guaranteed to stay alive for
    as long as there are active users of it.

    Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
    Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
    ---
    drivers/gpio/gpiolib.c | 71 +++++++++++++++++++++++++++----------
    include/linux/gpio/driver.h | 3 ++
    2 files changed, 56 insertions(+), 18 deletions(-)

    diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
    index f84ad54d8dbd..0371d23f0a46 100644
    --- a/drivers/gpio/gpiolib.c
    +++ b/drivers/gpio/gpiolib.c
    @@ -1014,16 +1014,10 @@ void gpiochip_remove(struct gpio_chip *gc)
    }
    EXPORT_SYMBOL_GPL(gpiochip_remove);

    -/**
    - * gpiochip_find() - iterator for locating a specific gpio_chip
    - * @data: data to pass to match function
    - * @match: Callback function to check gpio_chip
    +/*
    + * FIXME: This will be removed soon.
    *
    - * Similar to bus_find_device. It returns a reference to a gpio_chip as
    - * determined by a user supplied @match callback. The callback should return
    - * 0 if the device doesn't match and non-zero if it does. If the callback is
    - * non-zero, this function will return to the caller and not iterate over any
    - * more gpio_chips.
    + * This function is depracated, don't use.
    */
    struct gpio_chip *gpiochip_find(void *data,
    int (*match)(struct gpio_chip *gc,
    @@ -1031,21 +1025,62 @@ struct gpio_chip *gpiochip_find(void *data,
    {
    struct gpio_device *gdev;
    struct gpio_chip *gc = NULL;
    - unsigned long flags;

    - spin_lock_irqsave(&gpio_lock, flags);
    - list_for_each_entry(gdev, &gpio_devices, list)
    - if (gdev->chip && match(gdev->chip, data)) {
    - gc = gdev->chip;
    - break;
    - }
    -
    - spin_unlock_irqrestore(&gpio_lock, flags);
    + gdev = gpio_device_find(data, match);
    + if (gdev) {
    + gc = gdev->chip;
    + gpio_device_put(gdev);
    + }

    return gc;
    }
    EXPORT_SYMBOL_GPL(gpiochip_find);

    +/**
    + * gpio_device_find() - find a specific GPIO device
    + * @data: data to pass to match function
    + * @match: Callback function to check gpio_chip
    + *
    + * Returns:
    + * New reference to struct gpio_device.
    + *
    + * Similar to bus_find_device(). It returns a reference to a gpio_device as
    + * determined by a user supplied @match callback. The callback should return
    + * 0 if the device doesn't match and non-zero if it does. If the callback
    + * returns non-zero, this function will return to the caller and not iterate
    + * over any more gpio_devices.
    + *
    + * The callback takes the GPIO chip structure as argument. During the execution
    + * of the callback function the chip is protected from being freed. TODO: This
    + * actually has yet to be implemented.
    + *
    + * If the function returns non-NULL, the returned reference must be freed by
    + * the caller using gpio_device_put().
    + */
    +struct gpio_device *gpio_device_find(void *data,
    + int (*match)(struct gpio_chip *gc,
    + void *data))
    +{
    + struct gpio_device *gdev;
    +
    + /*
    + * Not yet but in the future the spinlock below will become a mutex.
    + * Annotate this function before anyone tries to use it in interrupt
    + * context like it happened with gpiochip_find().
    + */
    + might_sleep();
    +
    + guard(spinlock_irqsave)(&gpio_lock);
    +
    + list_for_each_entry(gdev, &gpio_devices, list) {
    + if (gdev->chip && match(gdev->chip, data))
    + return gpio_device_get(gdev);
    + }
    +
    + return NULL;
    +}
    +EXPORT_SYMBOL_GPL(gpio_device_find);
    +
    static int gpiochip_match_name(struct gpio_chip *gc, void *data)
    {
    const char *name = data;
    diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
    index 1cedbc3d3200..6ad1f1a8ef2e 100644
    --- a/include/linux/gpio/driver.h
    +++ b/include/linux/gpio/driver.h
    @@ -608,6 +608,9 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc,
    struct gpio_chip *gpiochip_find(void *data,
    int (*match)(struct gpio_chip *gc, void *data));

    +struct gpio_device *gpio_device_find(void *data,
    + int (*match)(struct gpio_chip *gc, void *data));
    +
    struct gpio_device *gpio_device_get(struct gpio_device *gdev);
    void gpio_device_put(struct gpio_device *gdev);

    --
    2.39.2
    \
     
     \ /
      Last update: 2023-09-15 17:06    [W:4.104 / U:0.056 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site