Commit cfb4be1a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'gpio-fixes-for-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

Pull gpio fixes from Bartosz Golaszewski:
 "Some last-minute fixes for this release from the GPIO subsystem.

  The first two address a regression in performance reported to me after
  the conversion to using SRCU in GPIOLIB that was merged during the
  v6.9 merge window. The second patch is not technically a fix but since
  after the first one we no longer need to use a per-descriptor SRCU
  struct, I think it's worth to simplify the code before it gets
  released on Sunday.

  The next two commits fix two memory issues: one use-after-free bug and
  one instance of possibly leaking kernel stack memory to user-space.

  Summary:

   - fix a performance regression in GPIO requesting and releasing after
     the conversion to SRCU

   - fix a use-after-free bug due to a race-condition

   - fix leaking stack memory to user-space in a GPIO uABI corner case"

* tag 'gpio-fixes-for-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
  gpiolib: cdev: fix uninitialised kfifo
  gpiolib: cdev: Fix use after free in lineinfo_changed_notify
  gpiolib: use a single SRCU struct for all GPIO descriptors
  gpiolib: fix the speed of descriptor label setting with SRCU
parents f4345f05 ee0166b6
...@@ -1193,6 +1193,8 @@ static int edge_detector_update(struct line *line, ...@@ -1193,6 +1193,8 @@ static int edge_detector_update(struct line *line,
struct gpio_v2_line_config *lc, struct gpio_v2_line_config *lc,
unsigned int line_idx, u64 edflags) unsigned int line_idx, u64 edflags)
{ {
u64 eflags;
int ret;
u64 active_edflags = READ_ONCE(line->edflags); u64 active_edflags = READ_ONCE(line->edflags);
unsigned int debounce_period_us = unsigned int debounce_period_us =
gpio_v2_line_config_debounce_period(lc, line_idx); gpio_v2_line_config_debounce_period(lc, line_idx);
...@@ -1204,6 +1206,18 @@ static int edge_detector_update(struct line *line, ...@@ -1204,6 +1206,18 @@ static int edge_detector_update(struct line *line,
/* sw debounced and still will be...*/ /* sw debounced and still will be...*/
if (debounce_period_us && READ_ONCE(line->sw_debounced)) { if (debounce_period_us && READ_ONCE(line->sw_debounced)) {
line_set_debounce_period(line, debounce_period_us); line_set_debounce_period(line, debounce_period_us);
/*
* ensure event fifo is initialised if edge detection
* is now enabled.
*/
eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS;
if (eflags && !kfifo_initialized(&line->req->events)) {
ret = kfifo_alloc(&line->req->events,
line->req->event_buffer_size,
GFP_KERNEL);
if (ret)
return ret;
}
return 0; return 0;
} }
...@@ -2351,7 +2365,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, ...@@ -2351,7 +2365,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
dflags = READ_ONCE(desc->flags); dflags = READ_ONCE(desc->flags);
scoped_guard(srcu, &desc->srcu) { scoped_guard(srcu, &desc->gdev->desc_srcu) {
label = gpiod_get_label(desc); label = gpiod_get_label(desc);
if (label && test_bit(FLAG_REQUESTED, &dflags)) if (label && test_bit(FLAG_REQUESTED, &dflags))
strscpy(info->consumer, label, strscpy(info->consumer, label,
...@@ -2799,11 +2813,11 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file) ...@@ -2799,11 +2813,11 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file)
struct gpio_chardev_data *cdev = file->private_data; struct gpio_chardev_data *cdev = file->private_data;
struct gpio_device *gdev = cdev->gdev; struct gpio_device *gdev = cdev->gdev;
bitmap_free(cdev->watched_lines);
blocking_notifier_chain_unregister(&gdev->device_notifier, blocking_notifier_chain_unregister(&gdev->device_notifier,
&cdev->device_unregistered_nb); &cdev->device_unregistered_nb);
blocking_notifier_chain_unregister(&gdev->line_state_notifier, blocking_notifier_chain_unregister(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb); &cdev->lineinfo_changed_nb);
bitmap_free(cdev->watched_lines);
gpio_device_put(gdev); gpio_device_put(gdev);
kfree(cdev); kfree(cdev);
......
...@@ -101,6 +101,7 @@ static bool gpiolib_initialized; ...@@ -101,6 +101,7 @@ static bool gpiolib_initialized;
const char *gpiod_get_label(struct gpio_desc *desc) const char *gpiod_get_label(struct gpio_desc *desc)
{ {
struct gpio_desc_label *label;
unsigned long flags; unsigned long flags;
flags = READ_ONCE(desc->flags); flags = READ_ONCE(desc->flags);
...@@ -108,23 +109,36 @@ const char *gpiod_get_label(struct gpio_desc *desc) ...@@ -108,23 +109,36 @@ const char *gpiod_get_label(struct gpio_desc *desc)
!test_bit(FLAG_REQUESTED, &flags)) !test_bit(FLAG_REQUESTED, &flags))
return "interrupt"; return "interrupt";
return test_bit(FLAG_REQUESTED, &flags) ? if (!test_bit(FLAG_REQUESTED, &flags))
srcu_dereference(desc->label, &desc->srcu) : NULL; return NULL;
label = srcu_dereference_check(desc->label, &desc->gdev->desc_srcu,
srcu_read_lock_held(&desc->gdev->desc_srcu));
return label->str;
}
static void desc_free_label(struct rcu_head *rh)
{
kfree(container_of(rh, struct gpio_desc_label, rh));
} }
static int desc_set_label(struct gpio_desc *desc, const char *label) static int desc_set_label(struct gpio_desc *desc, const char *label)
{ {
const char *new = NULL, *old; struct gpio_desc_label *new = NULL, *old;
if (label) { if (label) {
new = kstrdup_const(label, GFP_KERNEL); new = kzalloc(struct_size(new, str, strlen(label) + 1),
GFP_KERNEL);
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
strcpy(new->str, label);
} }
old = rcu_replace_pointer(desc->label, new, 1); old = rcu_replace_pointer(desc->label, new, 1);
synchronize_srcu(&desc->srcu); if (old)
kfree_const(old); call_srcu(&desc->gdev->desc_srcu, &old->rh, desc_free_label);
return 0; return 0;
} }
...@@ -695,10 +709,10 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid); ...@@ -695,10 +709,10 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
static void gpiodev_release(struct device *dev) static void gpiodev_release(struct device *dev)
{ {
struct gpio_device *gdev = to_gpio_device(dev); struct gpio_device *gdev = to_gpio_device(dev);
unsigned int i;
for (i = 0; i < gdev->ngpio; i++) /* Call pending kfree()s for descriptor labels. */
cleanup_srcu_struct(&gdev->descs[i].srcu); synchronize_srcu(&gdev->desc_srcu);
cleanup_srcu_struct(&gdev->desc_srcu);
ida_free(&gpio_ida, gdev->id); ida_free(&gpio_ida, gdev->id);
kfree_const(gdev->label); kfree_const(gdev->label);
...@@ -975,6 +989,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ...@@ -975,6 +989,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (ret) if (ret)
goto err_remove_from_list; goto err_remove_from_list;
ret = init_srcu_struct(&gdev->desc_srcu);
if (ret)
goto err_cleanup_gdev_srcu;
#ifdef CONFIG_PINCTRL #ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges); INIT_LIST_HEAD(&gdev->pin_ranges);
#endif #endif
...@@ -982,23 +1000,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ...@@ -982,23 +1000,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (gc->names) { if (gc->names) {
ret = gpiochip_set_desc_names(gc); ret = gpiochip_set_desc_names(gc);
if (ret) if (ret)
goto err_cleanup_gdev_srcu; goto err_cleanup_desc_srcu;
} }
ret = gpiochip_set_names(gc); ret = gpiochip_set_names(gc);
if (ret) if (ret)
goto err_cleanup_gdev_srcu; goto err_cleanup_desc_srcu;
ret = gpiochip_init_valid_mask(gc); ret = gpiochip_init_valid_mask(gc);
if (ret) if (ret)
goto err_cleanup_gdev_srcu; goto err_cleanup_desc_srcu;
for (desc_index = 0; desc_index < gc->ngpio; desc_index++) { for (desc_index = 0; desc_index < gc->ngpio; desc_index++) {
struct gpio_desc *desc = &gdev->descs[desc_index]; struct gpio_desc *desc = &gdev->descs[desc_index];
ret = init_srcu_struct(&desc->srcu);
if (ret)
goto err_cleanup_desc_srcu;
if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) { if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) {
assign_bit(FLAG_IS_OUT, assign_bit(FLAG_IS_OUT,
&desc->flags, !gc->get_direction(gc, desc_index)); &desc->flags, !gc->get_direction(gc, desc_index));
...@@ -1010,7 +1024,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ...@@ -1010,7 +1024,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
ret = of_gpiochip_add(gc); ret = of_gpiochip_add(gc);
if (ret) if (ret)
goto err_cleanup_desc_srcu; goto err_free_valid_mask;
ret = gpiochip_add_pin_ranges(gc); ret = gpiochip_add_pin_ranges(gc);
if (ret) if (ret)
...@@ -1057,10 +1071,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ...@@ -1057,10 +1071,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
gpiochip_remove_pin_ranges(gc); gpiochip_remove_pin_ranges(gc);
err_remove_of_chip: err_remove_of_chip:
of_gpiochip_remove(gc); of_gpiochip_remove(gc);
err_cleanup_desc_srcu: err_free_valid_mask:
while (desc_index--)
cleanup_srcu_struct(&gdev->descs[desc_index].srcu);
gpiochip_free_valid_mask(gc); gpiochip_free_valid_mask(gc);
err_cleanup_desc_srcu:
cleanup_srcu_struct(&gdev->desc_srcu);
err_cleanup_gdev_srcu: err_cleanup_gdev_srcu:
cleanup_srcu_struct(&gdev->srcu); cleanup_srcu_struct(&gdev->srcu);
err_remove_from_list: err_remove_from_list:
...@@ -2390,7 +2404,7 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset) ...@@ -2390,7 +2404,7 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset)
if (!test_bit(FLAG_REQUESTED, &desc->flags)) if (!test_bit(FLAG_REQUESTED, &desc->flags))
return NULL; return NULL;
guard(srcu)(&desc->srcu); guard(srcu)(&desc->gdev->desc_srcu);
label = kstrdup(gpiod_get_label(desc), GFP_KERNEL); label = kstrdup(gpiod_get_label(desc), GFP_KERNEL);
if (!label) if (!label)
...@@ -4781,7 +4795,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) ...@@ -4781,7 +4795,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
} }
for_each_gpio_desc(gc, desc) { for_each_gpio_desc(gc, desc) {
guard(srcu)(&desc->srcu); guard(srcu)(&desc->gdev->desc_srcu);
if (test_bit(FLAG_REQUESTED, &desc->flags)) { if (test_bit(FLAG_REQUESTED, &desc->flags)) {
gpiod_get_direction(desc); gpiod_get_direction(desc);
is_out = test_bit(FLAG_IS_OUT, &desc->flags); is_out = test_bit(FLAG_IS_OUT, &desc->flags);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
* @chip: pointer to the corresponding gpiochip, holding static * @chip: pointer to the corresponding gpiochip, holding static
* data for this device * data for this device
* @descs: array of ngpio descriptors. * @descs: array of ngpio descriptors.
* @desc_srcu: ensures consistent state of GPIO descriptors exposed to users
* @ngpio: the number of GPIO lines on this GPIO device, equal to the size * @ngpio: the number of GPIO lines on this GPIO device, equal to the size
* of the @descs array. * of the @descs array.
* @can_sleep: indicate whether the GPIO chip driver's callbacks can sleep * @can_sleep: indicate whether the GPIO chip driver's callbacks can sleep
...@@ -61,6 +62,7 @@ struct gpio_device { ...@@ -61,6 +62,7 @@ struct gpio_device {
struct module *owner; struct module *owner;
struct gpio_chip __rcu *chip; struct gpio_chip __rcu *chip;
struct gpio_desc *descs; struct gpio_desc *descs;
struct srcu_struct desc_srcu;
int base; int base;
u16 ngpio; u16 ngpio;
bool can_sleep; bool can_sleep;
...@@ -137,6 +139,11 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); ...@@ -137,6 +139,11 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
struct gpio_desc_label {
struct rcu_head rh;
char str[];
};
/** /**
* struct gpio_desc - Opaque descriptor for a GPIO * struct gpio_desc - Opaque descriptor for a GPIO
* *
...@@ -145,7 +152,6 @@ void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); ...@@ -145,7 +152,6 @@ void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
* @label: Name of the consumer * @label: Name of the consumer
* @name: Line name * @name: Line name
* @hog: Pointer to the device node that hogs this line (if any) * @hog: Pointer to the device node that hogs this line (if any)
* @srcu: SRCU struct protecting the label pointer.
* *
* These are obtained using gpiod_get() and are preferable to the old * These are obtained using gpiod_get() and are preferable to the old
* integer-based handles. * integer-based handles.
...@@ -177,13 +183,12 @@ struct gpio_desc { ...@@ -177,13 +183,12 @@ struct gpio_desc {
#define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */ #define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */
/* Connection label */ /* Connection label */
const char __rcu *label; struct gpio_desc_label __rcu *label;
/* Name of the GPIO */ /* Name of the GPIO */
const char *name; const char *name;
#ifdef CONFIG_OF_DYNAMIC #ifdef CONFIG_OF_DYNAMIC
struct device_node *hog; struct device_node *hog;
#endif #endif
struct srcu_struct srcu;
}; };
#define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) #define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)
...@@ -251,7 +256,7 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc) ...@@ -251,7 +256,7 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
#define gpiod_err(desc, fmt, ...) \ #define gpiod_err(desc, fmt, ...) \
do { \ do { \
scoped_guard(srcu, &desc->srcu) { \ scoped_guard(srcu, &desc->gdev->desc_srcu) { \
pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
} \ } \
...@@ -259,7 +264,7 @@ do { \ ...@@ -259,7 +264,7 @@ do { \
#define gpiod_warn(desc, fmt, ...) \ #define gpiod_warn(desc, fmt, ...) \
do { \ do { \
scoped_guard(srcu, &desc->srcu) { \ scoped_guard(srcu, &desc->gdev->desc_srcu) { \
pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
} \ } \
...@@ -267,7 +272,7 @@ do { \ ...@@ -267,7 +272,7 @@ do { \
#define gpiod_dbg(desc, fmt, ...) \ #define gpiod_dbg(desc, fmt, ...) \
do { \ do { \
scoped_guard(srcu, &desc->srcu) { \ scoped_guard(srcu, &desc->gdev->desc_srcu) { \
pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
} \ } \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment