Skip to content

Commit dcb73cb

Browse files
author
Bartosz Golaszewski
committed
gpio: cdev: use raw notifier for line state events
We use a notifier to implement the mechanism of informing the user-space about changes in GPIO line status. We register with the notifier when the GPIO character device file is opened and unregister when the last reference to the associated file descriptor is dropped. Since commit fcc8b63 ("gpiolib: switch the line state notifier to atomic") we use the atomic notifier variant. Atomic notifiers call rcu_synchronize in atomic_notifier_chain_unregister() which caused a significant performance regression in some circumstances, observed by user-space when calling close() on the GPIO device file descriptor. Replace the atomic notifier with the raw variant and provide synchronization with a read-write spinlock. Fixes: fcc8b63 ("gpiolib: switch the line state notifier to atomic") Reported-by: David Jander <david@protonic.nl> Closes: https://lore.kernel.org/all/20250311110034.53959031@erd003.prtnl/ Tested-by: David Jander <david@protonic.nl> Tested-by: Kent Gibson <warthog618@gmail.com> Link: https://lore.kernel.org/r/20250311-gpiolib-line-state-raw-notifier-v2-1-138374581e1e@linaro.org Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
1 parent 0102fbf commit dcb73cb

File tree

3 files changed

+18
-10
lines changed

3 files changed

+18
-10
lines changed

drivers/gpio/gpiolib-cdev.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2729,8 +2729,9 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
27292729
cdev->gdev = gpio_device_get(gdev);
27302730

27312731
cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify;
2732-
ret = atomic_notifier_chain_register(&gdev->line_state_notifier,
2733-
&cdev->lineinfo_changed_nb);
2732+
scoped_guard(write_lock_irqsave, &gdev->line_state_lock)
2733+
ret = raw_notifier_chain_register(&gdev->line_state_notifier,
2734+
&cdev->lineinfo_changed_nb);
27342735
if (ret)
27352736
goto out_free_bitmap;
27362737

@@ -2754,8 +2755,9 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
27542755
blocking_notifier_chain_unregister(&gdev->device_notifier,
27552756
&cdev->device_unregistered_nb);
27562757
out_unregister_line_notifier:
2757-
atomic_notifier_chain_unregister(&gdev->line_state_notifier,
2758-
&cdev->lineinfo_changed_nb);
2758+
scoped_guard(write_lock_irqsave, &gdev->line_state_lock)
2759+
raw_notifier_chain_unregister(&gdev->line_state_notifier,
2760+
&cdev->lineinfo_changed_nb);
27592761
out_free_bitmap:
27602762
gpio_device_put(gdev);
27612763
bitmap_free(cdev->watched_lines);
@@ -2779,8 +2781,9 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file)
27792781

27802782
blocking_notifier_chain_unregister(&gdev->device_notifier,
27812783
&cdev->device_unregistered_nb);
2782-
atomic_notifier_chain_unregister(&gdev->line_state_notifier,
2783-
&cdev->lineinfo_changed_nb);
2784+
scoped_guard(write_lock_irqsave, &gdev->line_state_lock)
2785+
raw_notifier_chain_unregister(&gdev->line_state_notifier,
2786+
&cdev->lineinfo_changed_nb);
27842787
bitmap_free(cdev->watched_lines);
27852788
gpio_device_put(gdev);
27862789
kfree(cdev);

drivers/gpio/gpiolib.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,7 +1025,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
10251025
}
10261026
}
10271027

1028-
ATOMIC_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
1028+
rwlock_init(&gdev->line_state_lock);
1029+
RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
10291030
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
10301031

10311032
ret = init_srcu_struct(&gdev->srcu);
@@ -4188,8 +4189,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);
41884189

41894190
void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action)
41904191
{
4191-
atomic_notifier_call_chain(&desc->gdev->line_state_notifier,
4192-
action, desc);
4192+
guard(read_lock_irqsave)(&desc->gdev->line_state_lock);
4193+
4194+
raw_notifier_call_chain(&desc->gdev->line_state_notifier, action, desc);
41934195
}
41944196

41954197
/**

drivers/gpio/gpiolib.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/gpio/driver.h>
1717
#include <linux/module.h>
1818
#include <linux/notifier.h>
19+
#include <linux/spinlock.h>
1920
#include <linux/srcu.h>
2021
#include <linux/workqueue.h>
2122

@@ -45,6 +46,7 @@
4546
* @list: links gpio_device:s together for traversal
4647
* @line_state_notifier: used to notify subscribers about lines being
4748
* requested, released or reconfigured
49+
* @line_state_lock: RW-spinlock protecting the line state notifier
4850
* @line_state_wq: used to emit line state events from a separate thread in
4951
* process context
5052
* @device_notifier: used to notify character device wait queues about the GPIO
@@ -72,7 +74,8 @@ struct gpio_device {
7274
const char *label;
7375
void *data;
7476
struct list_head list;
75-
struct atomic_notifier_head line_state_notifier;
77+
struct raw_notifier_head line_state_notifier;
78+
rwlock_t line_state_lock;
7679
struct workqueue_struct *line_state_wq;
7780
struct blocking_notifier_head device_notifier;
7881
struct srcu_struct srcu;

0 commit comments

Comments
 (0)