Commit 6efcfe10 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'gpio-fixes-for-v6.14-rc7' of git://git.kernel.org/pub/scm/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
parents b1144bc7 dcb73cba
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -2729,7 +2729,8 @@ 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,
	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;
@@ -2754,7 +2755,8 @@ 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,
	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);
@@ -2779,7 +2781,8 @@ 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,
	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);
+16 −19
Original line number Diff line number Diff line
@@ -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);
@@ -1056,25 +1057,20 @@ 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);
	if (ret)
@@ -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);
}

/**
+4 −1
Original line number Diff line number Diff line
@@ -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>

@@ -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
@@ -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;