Skip to content

Commit

Permalink
Merge tag 'gpio-fixes-for-v6.14-rc7' of git://git.kernel.org/pub/scm/…
Browse files Browse the repository at this point in the history
…linux/kernel/git/brgl/linux

Pull gpio fixes from Bartosz Golaszewski:
 "The first fix is a backport from my v6.15-rc1 queue that turned out to
  be needed in v6.14 as well but as the former diverged from my fixes
  branch I had to adjust the patch a bit.

  The second one fixes a regression observed in user-space where closing
  a file descriptor associated with a GPIO device results in a ~10ms
  delay due to the atomic notifier calling rcu_synchronize() when
  unregistering.

  Summary:

   - don't check the return value of gpio_chip::get_direction() when
     registering a GPIO chip

   - use raw notifier for line state events"

* tag 'gpio-fixes-for-v6.14-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
  gpio: cdev: use raw notifier for line state events
  gpiolib: don't check the retval of get_direction() when registering a chip
  • Loading branch information
Linus Torvalds committed Mar 14, 2025
2 parents b1144bc + dcb73cb commit 6efcfe1
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 26 deletions.
15 changes: 9 additions & 6 deletions drivers/gpio/gpiolib-cdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -2729,8 +2729,9 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
cdev->gdev = gpio_device_get(gdev);

cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify;
ret = atomic_notifier_chain_register(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb);
scoped_guard(write_lock_irqsave, &gdev->line_state_lock)
ret = raw_notifier_chain_register(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb);
if (ret)
goto out_free_bitmap;

Expand All @@ -2754,8 +2755,9 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
blocking_notifier_chain_unregister(&gdev->device_notifier,
&cdev->device_unregistered_nb);
out_unregister_line_notifier:
atomic_notifier_chain_unregister(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb);
scoped_guard(write_lock_irqsave, &gdev->line_state_lock)
raw_notifier_chain_unregister(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb);
out_free_bitmap:
gpio_device_put(gdev);
bitmap_free(cdev->watched_lines);
Expand All @@ -2779,8 +2781,9 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file)

blocking_notifier_chain_unregister(&gdev->device_notifier,
&cdev->device_unregistered_nb);
atomic_notifier_chain_unregister(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb);
scoped_guard(write_lock_irqsave, &gdev->line_state_lock)
raw_notifier_chain_unregister(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb);
bitmap_free(cdev->watched_lines);
gpio_device_put(gdev);
kfree(cdev);
Expand Down
35 changes: 16 additions & 19 deletions drivers/gpio/gpiolib.c
Original file line number Diff line number Diff line change
Expand Up @@ -1025,7 +1025,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
}
}

ATOMIC_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
rwlock_init(&gdev->line_state_lock);
RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);

ret = init_srcu_struct(&gdev->srcu);
Expand Down Expand Up @@ -1056,24 +1057,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,

desc->gdev = gdev;

if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) {
ret = gc->get_direction(gc, desc_index);
if (ret < 0)
/*
* FIXME: Bail-out here once all GPIO drivers
* are updated to not return errors in
* situations that can be considered normal
* operation.
*/
dev_warn(&gdev->dev,
"%s: get_direction failed: %d\n",
__func__, ret);

assign_bit(FLAG_IS_OUT, &desc->flags, !ret);
} else {
/*
* We would typically want to check the return value of
* get_direction() here but we must not check the return value
* and bail-out as pin controllers can have pins configured to
* alternate functions and return -EINVAL. Also: there's no
* need to take the SRCU lock here.
*/
if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index))
assign_bit(FLAG_IS_OUT, &desc->flags,
!gc->get_direction(gc, desc_index));
else
assign_bit(FLAG_IS_OUT,
&desc->flags, !gc->direction_input);
}
}

ret = of_gpiochip_add(gc);
Expand Down Expand Up @@ -4193,8 +4189,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action)
{
atomic_notifier_call_chain(&desc->gdev->line_state_notifier,
action, desc);
guard(read_lock_irqsave)(&desc->gdev->line_state_lock);

raw_notifier_call_chain(&desc->gdev->line_state_notifier, action, desc);
}

/**
Expand Down
5 changes: 4 additions & 1 deletion drivers/gpio/gpiolib.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/spinlock.h>
#include <linux/srcu.h>
#include <linux/workqueue.h>

Expand Down Expand Up @@ -45,6 +46,7 @@
* @list: links gpio_device:s together for traversal
* @line_state_notifier: used to notify subscribers about lines being
* requested, released or reconfigured
* @line_state_lock: RW-spinlock protecting the line state notifier
* @line_state_wq: used to emit line state events from a separate thread in
* process context
* @device_notifier: used to notify character device wait queues about the GPIO
Expand Down Expand Up @@ -72,7 +74,8 @@ struct gpio_device {
const char *label;
void *data;
struct list_head list;
struct atomic_notifier_head line_state_notifier;
struct raw_notifier_head line_state_notifier;
rwlock_t line_state_lock;
struct workqueue_struct *line_state_wq;
struct blocking_notifier_head device_notifier;
struct srcu_struct srcu;
Expand Down

0 comments on commit 6efcfe1

Please sign in to comment.