linux-gpio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/8] gpio: notify user-space about config changes in the kernel
@ 2024-10-17  8:14 Bartosz Golaszewski
  2024-10-17  8:14 ` [PATCH v4 1/8] gpiolib: notify user-space when a driver requests its own desc Bartosz Golaszewski
                   ` (7 more replies)
  0 siblings, 8 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17  8:14 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Kent Gibson
  Cc: linux-gpio, linux-kernel, Bartosz Golaszewski

We currently only emit events on changed line config to user-space on
changes made from user-space. Users have no way of getting notified
about in-kernel changes. This series improves the situation and also
contains a couple other related improvements.

This is a reworked approach which gets and stores as much info (all of
it actually, except for the pinctrl state) the moment the event occurrs
but moves the actual queueing of the event on the kfifo to a dedicated
high-priority, ordered workqueue.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
Changes in v4:
- don't emit additional events when emulating open-source or open-drain
  from gpiod_direction_output(), to that end use the nonotify variants
  of gpiod_direction_input()
- store current debounce period in the GPIO line descriptor and remove
  the entire supinfo infrastructure from gpiolib-cdev.c
- fix all (hopefully) corner-cases where two reconfigure or
  requested/reconfigured events could be emitted for a single logical
  event
- emit the reconfigure event from gpio_set_debounce_timeout()
- add a patch refactoring gpio_do_set_config()
- Link to v3: https://lore.kernel.org/r/20241015-gpio-notify-in-kernel-events-v3-0-9edf05802271@linaro.org

Changes in v3:
- don't reach into pinctrl if the USED flag is already set
- reword the comment from patch 5 and move it one patch back
- don't abandon emitting the event if the GPIO chip is gone by the time
  the work is executed, just don't reach into pinctrl and return
  whatever data we already had
- Link to v2: https://lore.kernel.org/r/20241010-gpio-notify-in-kernel-events-v2-0-b560411f7c59@linaro.org

Changes in v2:
- put all line-info events emitting on a workqueue to allow queueing
  them from atomic context
- switch the notifier type used for emitting info events to atomic
- add a patch notifying user-space about drivers requesting their own
  descs
- drop patches that were picked up
- Link to v1: https://lore.kernel.org/r/20241004-gpio-notify-in-kernel-events-v1-0-8ac29e1df4fe@linaro.org

---
Bartosz Golaszewski (8):
      gpiolib: notify user-space when a driver requests its own desc
      gpiolib: unduplicate chip guard in set_config path
      gpio: cdev: go back to storing debounce period in the GPIO descriptor
      gpio: cdev: prepare gpio_desc_to_lineinfo() for being called from atomic
      gpiolib: add a per-gpio_device line state notification workqueue
      gpio: cdev: put emitting the line state events on a workqueue
      gpiolib: switch the line state notifier to atomic
      gpiolib: notify user-space about in-kernel line state changes

 drivers/gpio/gpiolib-cdev.c | 297 ++++++++++++++++++--------------------------
 drivers/gpio/gpiolib.c      | 130 ++++++++++++++-----
 drivers/gpio/gpiolib.h      |  14 ++-
 3 files changed, 239 insertions(+), 202 deletions(-)
---
base-commit: 15e7d45e786a62a211dd0098fee7c57f84f8c681
change-id: 20240924-gpio-notify-in-kernel-events-07cd912153e8

Best regards,
-- 
Bartosz Golaszewski <bartosz.golaszewski@linaro.org>


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v4 1/8] gpiolib: notify user-space when a driver requests its own desc
  2024-10-17  8:14 [PATCH v4 0/8] gpio: notify user-space about config changes in the kernel Bartosz Golaszewski
@ 2024-10-17  8:14 ` Bartosz Golaszewski
  2024-10-17  8:14 ` [PATCH v4 2/8] gpiolib: unduplicate chip guard in set_config path Bartosz Golaszewski
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17  8:14 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Kent Gibson
  Cc: linux-gpio, linux-kernel, Bartosz Golaszewski

From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

We notify user-space about lines being requested from user-space or by
drivers calling gpiod_get() but not when drivers request their own lines
so add the missing call to gpiod_line_state_notify().

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
 drivers/gpio/gpiolib.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 97346b746ef5..c09464f70f73 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2532,6 +2532,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc,
 		return ERR_PTR(ret);
 	}
 
+	gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
+
 	return desc;
 }
 EXPORT_SYMBOL_GPL(gpiochip_request_own_desc);

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 2/8] gpiolib: unduplicate chip guard in set_config path
  2024-10-17  8:14 [PATCH v4 0/8] gpio: notify user-space about config changes in the kernel Bartosz Golaszewski
  2024-10-17  8:14 ` [PATCH v4 1/8] gpiolib: notify user-space when a driver requests its own desc Bartosz Golaszewski
@ 2024-10-17  8:14 ` Bartosz Golaszewski
  2024-10-17  8:14 ` [PATCH v4 3/8] gpio: cdev: go back to storing debounce period in the GPIO descriptor Bartosz Golaszewski
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17  8:14 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Kent Gibson
  Cc: linux-gpio, linux-kernel, Bartosz Golaszewski

From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

We don't need to guard the GPIO chip until its first dereference in
gpio_do_set_config().

First: change the prototype of gpio_do_set_config() to take the GPIO
line descriptor as argument, then move the gpio_chip protection into it
and drop it in two places where it's done too early.

This has the added benefit of making gpio_go_set_config() safe to use
from outside of this compilation unit without taking the gdev SRCU read
lock and will come in handy when we'll want to make it available to the
character device code.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
 drivers/gpio/gpiolib.c | 23 +++++++++--------------
 1 file changed, 9 insertions(+), 14 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c09464f70f73..b1ce213d3a23 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2562,13 +2562,16 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
  * rely on gpio_request() having been called beforehand.
  */
 
-static int gpio_do_set_config(struct gpio_chip *gc, unsigned int offset,
-			      unsigned long config)
+static int gpio_do_set_config(struct gpio_desc *desc, unsigned long config)
 {
-	if (!gc->set_config)
+	CLASS(gpio_chip_guard, guard)(desc);
+	if (!guard.gc)
+		return -ENODEV;
+
+	if (!guard.gc->set_config)
 		return -ENOTSUPP;
 
-	return gc->set_config(gc, offset, config);
+	return guard.gc->set_config(guard.gc, gpio_chip_hwgpio(desc), config);
 }
 
 static int gpio_set_config_with_argument(struct gpio_desc *desc,
@@ -2577,12 +2580,8 @@ static int gpio_set_config_with_argument(struct gpio_desc *desc,
 {
 	unsigned long config;
 
-	CLASS(gpio_chip_guard, guard)(desc);
-	if (!guard.gc)
-		return -ENODEV;
-
 	config = pinconf_to_config_packed(mode, argument);
-	return gpio_do_set_config(guard.gc, gpio_chip_hwgpio(desc), config);
+	return gpio_do_set_config(desc, config);
 }
 
 static int gpio_set_config_with_argument_optional(struct gpio_desc *desc,
@@ -2944,11 +2943,7 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config)
 {
 	VALIDATE_DESC(desc);
 
-	CLASS(gpio_chip_guard, guard)(desc);
-	if (!guard.gc)
-		return -ENODEV;
-
-	return gpio_do_set_config(guard.gc, gpio_chip_hwgpio(desc), config);
+	return gpio_do_set_config(desc, config);
 }
 EXPORT_SYMBOL_GPL(gpiod_set_config);
 

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 3/8] gpio: cdev: go back to storing debounce period in the GPIO descriptor
  2024-10-17  8:14 [PATCH v4 0/8] gpio: notify user-space about config changes in the kernel Bartosz Golaszewski
  2024-10-17  8:14 ` [PATCH v4 1/8] gpiolib: notify user-space when a driver requests its own desc Bartosz Golaszewski
  2024-10-17  8:14 ` [PATCH v4 2/8] gpiolib: unduplicate chip guard in set_config path Bartosz Golaszewski
@ 2024-10-17  8:14 ` Bartosz Golaszewski
  2024-10-17 12:44   ` Kent Gibson
  2024-10-17  8:14 ` [PATCH v4 4/8] gpio: cdev: prepare gpio_desc_to_lineinfo() for being called from atomic Bartosz Golaszewski
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17  8:14 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Kent Gibson
  Cc: linux-gpio, linux-kernel, Bartosz Golaszewski

From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

This effectively reverts commits 9344e34e7992 ("gpiolib: cdev: relocate
debounce_period_us from struct gpio_desc") and d8543cbaf979 ("gpiolib:
remove debounce_period_us from struct gpio_desc") and goes back to
storing the debounce period in microseconds in the GPIO descriptor

We're doing it in preparation for notifying the user-space about
in-kernel line config changes.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
 drivers/gpio/gpiolib-cdev.c | 163 ++++++--------------------------------------
 drivers/gpio/gpiolib.c      |  18 ++++-
 drivers/gpio/gpiolib.h      |   5 ++
 3 files changed, 43 insertions(+), 143 deletions(-)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index b0050250ac3a..d55d2a246d41 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -23,7 +23,6 @@
 #include <linux/overflow.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/poll.h>
-#include <linux/rbtree.h>
 #include <linux/seq_file.h>
 #include <linux/spinlock.h>
 #include <linux/timekeeping.h>
@@ -421,7 +420,6 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
 
 /**
  * struct line - contains the state of a requested line
- * @node: to store the object in supinfo_tree if supplemental
  * @desc: the GPIO descriptor for this line.
  * @req: the corresponding line request
  * @irq: the interrupt triggered in response to events on this GPIO
@@ -434,7 +432,6 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
  * @line_seqno: the seqno for the current edge event in the sequence of
  * events for this line.
  * @work: the worker that implements software debouncing
- * @debounce_period_us: the debounce period in microseconds
  * @sw_debounced: flag indicating if the software debouncer is active
  * @level: the current debounced physical level of the line
  * @hdesc: the Hardware Timestamp Engine (HTE) descriptor
@@ -443,7 +440,6 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
  * @last_seqno: the last sequence number before debounce period expires
  */
 struct line {
-	struct rb_node node;
 	struct gpio_desc *desc;
 	/*
 	 * -- edge detector specific fields --
@@ -477,15 +473,6 @@ struct line {
 	 * -- debouncer specific fields --
 	 */
 	struct delayed_work work;
-	/*
-	 * debounce_period_us is accessed by debounce_irq_handler() and
-	 * process_hw_ts() which are disabled when modified by
-	 * debounce_setup(), edge_detector_setup() or edge_detector_stop()
-	 * or can live with a stale version when updated by
-	 * edge_detector_update().
-	 * The modifying functions are themselves mutually exclusive.
-	 */
-	unsigned int debounce_period_us;
 	/*
 	 * sw_debounce is accessed by linereq_set_config(), which is the
 	 * only setter, and linereq_get_values(), which can live with a
@@ -518,17 +505,6 @@ struct line {
 #endif /* CONFIG_HTE */
 };
 
-/*
- * a rbtree of the struct lines containing supplemental info.
- * Used to populate gpio_v2_line_info with cdev specific fields not contained
- * in the struct gpio_desc.
- * A line is determined to contain supplemental information by
- * line_has_supinfo().
- */
-static struct rb_root supinfo_tree = RB_ROOT;
-/* covers supinfo_tree */
-static DEFINE_SPINLOCK(supinfo_lock);
-
 /**
  * struct linereq - contains the state of a userspace line request
  * @gdev: the GPIO device the line request pertains to
@@ -542,8 +518,7 @@ static DEFINE_SPINLOCK(supinfo_lock);
  * this line request.  Note that this is not used when @num_lines is 1, as
  * the line_seqno is then the same and is cheaper to calculate.
  * @config_mutex: mutex for serializing ioctl() calls to ensure consistency
- * of configuration, particularly multi-step accesses to desc flags and
- * changes to supinfo status.
+ * of configuration, particularly multi-step accesses to desc flags.
  * @lines: the lines held by this line request, with @num_lines elements.
  */
 struct linereq {
@@ -559,103 +534,6 @@ struct linereq {
 	struct line lines[] __counted_by(num_lines);
 };
 
-static void supinfo_insert(struct line *line)
-{
-	struct rb_node **new = &(supinfo_tree.rb_node), *parent = NULL;
-	struct line *entry;
-
-	guard(spinlock)(&supinfo_lock);
-
-	while (*new) {
-		entry = container_of(*new, struct line, node);
-
-		parent = *new;
-		if (line->desc < entry->desc) {
-			new = &((*new)->rb_left);
-		} else if (line->desc > entry->desc) {
-			new = &((*new)->rb_right);
-		} else {
-			/* this should never happen */
-			WARN(1, "duplicate line inserted");
-			return;
-		}
-	}
-
-	rb_link_node(&line->node, parent, new);
-	rb_insert_color(&line->node, &supinfo_tree);
-}
-
-static void supinfo_erase(struct line *line)
-{
-	guard(spinlock)(&supinfo_lock);
-
-	rb_erase(&line->node, &supinfo_tree);
-}
-
-static struct line *supinfo_find(struct gpio_desc *desc)
-{
-	struct rb_node *node = supinfo_tree.rb_node;
-	struct line *line;
-
-	while (node) {
-		line = container_of(node, struct line, node);
-		if (desc < line->desc)
-			node = node->rb_left;
-		else if (desc > line->desc)
-			node = node->rb_right;
-		else
-			return line;
-	}
-	return NULL;
-}
-
-static void supinfo_to_lineinfo(struct gpio_desc *desc,
-				struct gpio_v2_line_info *info)
-{
-	struct gpio_v2_line_attribute *attr;
-	struct line *line;
-
-	guard(spinlock)(&supinfo_lock);
-
-	line = supinfo_find(desc);
-	if (!line)
-		return;
-
-	attr = &info->attrs[info->num_attrs];
-	attr->id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE;
-	attr->debounce_period_us = READ_ONCE(line->debounce_period_us);
-	info->num_attrs++;
-}
-
-static inline bool line_has_supinfo(struct line *line)
-{
-	return READ_ONCE(line->debounce_period_us);
-}
-
-/*
- * Checks line_has_supinfo() before and after the change to avoid unnecessary
- * supinfo_tree access.
- * Called indirectly by linereq_create() or linereq_set_config() so line
- * is already protected from concurrent changes.
- */
-static void line_set_debounce_period(struct line *line,
-				     unsigned int debounce_period_us)
-{
-	bool was_suppl = line_has_supinfo(line);
-
-	WRITE_ONCE(line->debounce_period_us, debounce_period_us);
-
-	/* if supinfo status is unchanged then we're done */
-	if (line_has_supinfo(line) == was_suppl)
-		return;
-
-	/* supinfo status has changed, so update the tree */
-	if (was_suppl)
-		supinfo_erase(line);
-	else
-		supinfo_insert(line);
-}
-
 #define GPIO_V2_LINE_BIAS_FLAGS \
 	(GPIO_V2_LINE_FLAG_BIAS_PULL_UP | \
 	 GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN | \
@@ -823,7 +701,7 @@ static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p)
 		line->total_discard_seq++;
 		line->last_seqno = ts->seq;
 		mod_delayed_work(system_wq, &line->work,
-		  usecs_to_jiffies(READ_ONCE(line->debounce_period_us)));
+		  usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
 	} else {
 		if (unlikely(ts->seq < line->line_seqno))
 			return HTE_CB_HANDLED;
@@ -964,7 +842,7 @@ static irqreturn_t debounce_irq_handler(int irq, void *p)
 	struct line *line = p;
 
 	mod_delayed_work(system_wq, &line->work,
-		usecs_to_jiffies(READ_ONCE(line->debounce_period_us)));
+		usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
 
 	return IRQ_HANDLED;
 }
@@ -1047,7 +925,7 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us)
 	/* try hardware */
 	ret = gpiod_set_debounce(line->desc, debounce_period_us);
 	if (!ret) {
-		line_set_debounce_period(line, debounce_period_us);
+		WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
 		return ret;
 	}
 	if (ret != -ENOTSUPP)
@@ -1132,7 +1010,8 @@ static void edge_detector_stop(struct line *line)
 	cancel_delayed_work_sync(&line->work);
 	WRITE_ONCE(line->sw_debounced, 0);
 	WRITE_ONCE(line->edflags, 0);
-	line_set_debounce_period(line, 0);
+	if (line->desc)
+		WRITE_ONCE(line->desc->debounce_period_us, 0);
 	/* do not change line->level - see comment in debounced_value() */
 }
 
@@ -1165,7 +1044,7 @@ static int edge_detector_setup(struct line *line,
 		ret = debounce_setup(line, debounce_period_us);
 		if (ret)
 			return ret;
-		line_set_debounce_period(line, debounce_period_us);
+		WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
 	}
 
 	/* detection disabled or sw debouncer will provide edge detection */
@@ -1213,12 +1092,12 @@ static int edge_detector_update(struct line *line,
 			gpio_v2_line_config_debounce_period(lc, line_idx);
 
 	if ((active_edflags == edflags) &&
-	    (READ_ONCE(line->debounce_period_us) == debounce_period_us))
+	    (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us))
 		return 0;
 
 	/* sw debounced and still will be...*/
 	if (debounce_period_us && READ_ONCE(line->sw_debounced)) {
-		line_set_debounce_period(line, debounce_period_us);
+		WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
 		/*
 		 * ensure event fifo is initialised if edge detection
 		 * is now enabled.
@@ -1677,7 +1556,6 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
 
 static void linereq_free(struct linereq *lr)
 {
-	struct line *line;
 	unsigned int i;
 
 	if (lr->device_unregistered_nb.notifier_call)
@@ -1685,14 +1563,10 @@ static void linereq_free(struct linereq *lr)
 						   &lr->device_unregistered_nb);
 
 	for (i = 0; i < lr->num_lines; i++) {
-		line = &lr->lines[i];
-		if (!line->desc)
-			continue;
-
-		edge_detector_stop(line);
-		if (line_has_supinfo(line))
-			supinfo_erase(line);
-		gpiod_free(line->desc);
+		if (lr->lines[i].desc) {
+			edge_detector_stop(&lr->lines[i]);
+			gpiod_free(lr->lines[i].desc);
+		}
 	}
 	kfifo_free(&lr->events);
 	kfree(lr->label);
@@ -2363,6 +2237,7 @@ static void gpio_v2_line_info_changed_to_v1(
 static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
 				  struct gpio_v2_line_info *info)
 {
+	u32 debounce_period_us;
 	unsigned long dflags;
 	const char *label;
 
@@ -2435,6 +2310,14 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
 		info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME;
 	else if (test_bit(FLAG_EVENT_CLOCK_HTE, &dflags))
 		info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE;
+
+	debounce_period_us = READ_ONCE(desc->debounce_period_us);
+	if (debounce_period_us) {
+		info->attrs[info->num_attrs].id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE;
+		info->attrs[info->num_attrs].debounce_period_us =
+							debounce_period_us;
+		info->num_attrs++;
+	}
 }
 
 struct gpio_chardev_data {
@@ -2540,7 +2423,6 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
 			return -EBUSY;
 	}
 	gpio_desc_to_lineinfo(desc, &lineinfo);
-	supinfo_to_lineinfo(desc, &lineinfo);
 
 	if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
 		if (watch)
@@ -2633,7 +2515,6 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
 	chg.event_type = action;
 	chg.timestamp_ns = ktime_get_ns();
 	gpio_desc_to_lineinfo(desc, &chg.info);
-	supinfo_to_lineinfo(desc, &chg.info);
 
 	ret = kfifo_in_spinlocked(&cdev->events, &chg, 1, &cdev->wait.lock);
 	if (ret)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index b1ce213d3a23..807bee86afd5 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2432,7 +2432,9 @@ static void gpiod_free_commit(struct gpio_desc *desc)
 #endif
 		desc_set_label(desc, NULL);
 		WRITE_ONCE(desc->flags, flags);
-
+#ifdef CONFIG_GPIO_CDEV
+		WRITE_ONCE(desc->debounce_period_us, 0);
+#endif
 		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_RELEASED);
 	}
 }
@@ -2564,6 +2566,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
 
 static int gpio_do_set_config(struct gpio_desc *desc, unsigned long config)
 {
+	int ret;
+
 	CLASS(gpio_chip_guard, guard)(desc);
 	if (!guard.gc)
 		return -ENODEV;
@@ -2571,7 +2575,17 @@ static int gpio_do_set_config(struct gpio_desc *desc, unsigned long config)
 	if (!guard.gc->set_config)
 		return -ENOTSUPP;
 
-	return guard.gc->set_config(guard.gc, gpio_chip_hwgpio(desc), config);
+	ret = guard.gc->set_config(guard.gc, gpio_chip_hwgpio(desc), config);
+#ifdef CONFIG_GPIO_CDEV
+	/*
+	 * Special case - if we're setting debounce period, we need to store
+	 * it in the descriptor in case user-space wants to know it.
+	 */
+	if (!ret && pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE)
+		WRITE_ONCE(desc->debounce_period_us,
+			   pinconf_to_config_argument(config));
+#endif
+	return ret;
 }
 
 static int gpio_set_config_with_argument(struct gpio_desc *desc,
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 067197d61d57..8daba06eb472 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -165,6 +165,7 @@ struct gpio_desc_label {
  * @label:		Name of the consumer
  * @name:		Line name
  * @hog:		Pointer to the device node that hogs this line (if any)
+ * @debounce_period_us:	Debounce period in microseconds
  *
  * These are obtained using gpiod_get() and are preferable to the old
  * integer-based handles.
@@ -202,6 +203,10 @@ struct gpio_desc {
 #ifdef CONFIG_OF_DYNAMIC
 	struct device_node	*hog;
 #endif
+#ifdef CONFIG_GPIO_CDEV
+	/* debounce period in microseconds */
+	unsigned int		debounce_period_us;
+#endif
 };
 
 #define gpiod_not_found(desc)		(IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 4/8] gpio: cdev: prepare gpio_desc_to_lineinfo() for being called from atomic
  2024-10-17  8:14 [PATCH v4 0/8] gpio: notify user-space about config changes in the kernel Bartosz Golaszewski
                   ` (2 preceding siblings ...)
  2024-10-17  8:14 ` [PATCH v4 3/8] gpio: cdev: go back to storing debounce period in the GPIO descriptor Bartosz Golaszewski
@ 2024-10-17  8:14 ` Bartosz Golaszewski
  2024-10-17  8:14 ` [PATCH v4 5/8] gpiolib: add a per-gpio_device line state notification workqueue Bartosz Golaszewski
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17  8:14 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Kent Gibson
  Cc: linux-gpio, linux-kernel, Bartosz Golaszewski

From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

In order to prepare gpio_desc_to_lineinfo() to being called from atomic
context, add a new argument - bool atomic - which, if set, indicates
that no sleeping functions must be called (currently: only
pinctrl_gpio_can_use_line()).

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
 drivers/gpio/gpiolib-cdev.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index d55d2a246d41..0cba74381687 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2235,7 +2235,7 @@ static void gpio_v2_line_info_changed_to_v1(
 #endif /* CONFIG_GPIO_CDEV_V1 */
 
 static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
-				  struct gpio_v2_line_info *info)
+				  struct gpio_v2_line_info *info, bool atomic)
 {
 	u32 debounce_period_us;
 	unsigned long dflags;
@@ -2277,9 +2277,12 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
 	    test_bit(FLAG_USED_AS_IRQ, &dflags) ||
 	    test_bit(FLAG_EXPORT, &dflags) ||
 	    test_bit(FLAG_SYSFS, &dflags) ||
-	    !gpiochip_line_is_valid(guard.gc, info->offset) ||
-	    !pinctrl_gpio_can_use_line(guard.gc, info->offset))
+	    !gpiochip_line_is_valid(guard.gc, info->offset)) {
 		info->flags |= GPIO_V2_LINE_FLAG_USED;
+	} else if (!atomic) {
+		if (!pinctrl_gpio_can_use_line(guard.gc, info->offset))
+			info->flags |= GPIO_V2_LINE_FLAG_USED;
+	}
 
 	if (test_bit(FLAG_IS_OUT, &dflags))
 		info->flags |= GPIO_V2_LINE_FLAG_OUTPUT;
@@ -2385,7 +2388,7 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip,
 			return -EBUSY;
 	}
 
-	gpio_desc_to_lineinfo(desc, &lineinfo_v2);
+	gpio_desc_to_lineinfo(desc, &lineinfo_v2, false);
 	gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
 
 	if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
@@ -2422,7 +2425,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
 		if (test_and_set_bit(lineinfo.offset, cdev->watched_lines))
 			return -EBUSY;
 	}
-	gpio_desc_to_lineinfo(desc, &lineinfo);
+	gpio_desc_to_lineinfo(desc, &lineinfo, false);
 
 	if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
 		if (watch)
@@ -2514,7 +2517,7 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
 	memset(&chg, 0, sizeof(chg));
 	chg.event_type = action;
 	chg.timestamp_ns = ktime_get_ns();
-	gpio_desc_to_lineinfo(desc, &chg.info);
+	gpio_desc_to_lineinfo(desc, &chg.info, false);
 
 	ret = kfifo_in_spinlocked(&cdev->events, &chg, 1, &cdev->wait.lock);
 	if (ret)

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 5/8] gpiolib: add a per-gpio_device line state notification workqueue
  2024-10-17  8:14 [PATCH v4 0/8] gpio: notify user-space about config changes in the kernel Bartosz Golaszewski
                   ` (3 preceding siblings ...)
  2024-10-17  8:14 ` [PATCH v4 4/8] gpio: cdev: prepare gpio_desc_to_lineinfo() for being called from atomic Bartosz Golaszewski
@ 2024-10-17  8:14 ` Bartosz Golaszewski
  2024-10-17  8:14 ` [PATCH v4 6/8] gpio: cdev: put emitting the line state events on a workqueue Bartosz Golaszewski
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17  8:14 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Kent Gibson
  Cc: linux-gpio, linux-kernel, Bartosz Golaszewski

From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

In order to prepare the line state notification mechanism for working in
atomic context as well, add a dedicated, high-priority, ordered
workqueue to GPIO device which will be used to queue the events fron any
context for them to be emitted always in process context.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
 drivers/gpio/gpiolib-cdev.c | 6 ++++++
 drivers/gpio/gpiolib.h      | 4 ++++
 2 files changed, 10 insertions(+)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 0cba74381687..b242fdb1ad28 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2749,6 +2749,11 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
 	gdev->chrdev.owner = THIS_MODULE;
 	gdev->dev.devt = MKDEV(MAJOR(devt), gdev->id);
 
+	gdev->line_state_wq = alloc_ordered_workqueue(dev_name(&gdev->dev),
+						      WQ_HIGHPRI);
+	if (!gdev->line_state_wq)
+		return -ENOMEM;
+
 	ret = cdev_device_add(&gdev->chrdev, &gdev->dev);
 	if (ret)
 		return ret;
@@ -2765,6 +2770,7 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
 
 void gpiolib_cdev_unregister(struct gpio_device *gdev)
 {
+	destroy_workqueue(gdev->line_state_wq);
 	cdev_device_del(&gdev->chrdev, &gdev->dev);
 	blocking_notifier_call_chain(&gdev->device_notifier, 0, NULL);
 }
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 8daba06eb472..8737e1641278 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/notifier.h>
 #include <linux/srcu.h>
+#include <linux/workqueue.h>
 
 #define GPIOCHIP_NAME	"gpiochip"
 
@@ -44,6 +45,8 @@
  * @list: links gpio_device:s together for traversal
  * @line_state_notifier: used to notify subscribers about lines being
  *                       requested, released or reconfigured
+ * @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
  *                   device being unregistered
  * @srcu: protects the pointer to the underlying GPIO chip
@@ -70,6 +73,7 @@ struct gpio_device {
 	void			*data;
 	struct list_head        list;
 	struct blocking_notifier_head line_state_notifier;
+	struct workqueue_struct	*line_state_wq;
 	struct blocking_notifier_head device_notifier;
 	struct srcu_struct	srcu;
 

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 6/8] gpio: cdev: put emitting the line state events on a workqueue
  2024-10-17  8:14 [PATCH v4 0/8] gpio: notify user-space about config changes in the kernel Bartosz Golaszewski
                   ` (4 preceding siblings ...)
  2024-10-17  8:14 ` [PATCH v4 5/8] gpiolib: add a per-gpio_device line state notification workqueue Bartosz Golaszewski
@ 2024-10-17  8:14 ` Bartosz Golaszewski
  2024-10-17  8:14 ` [PATCH v4 7/8] gpiolib: switch the line state notifier to atomic Bartosz Golaszewski
  2024-10-17  8:14 ` [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes Bartosz Golaszewski
  7 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17  8:14 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Kent Gibson
  Cc: linux-gpio, linux-kernel, Bartosz Golaszewski

From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

In order to allow line state notifications to be emitted from atomic
context (for instance: from gpiod_direction_input/output()), we must
stop calling any sleeping functions in lineinfo_changed_notify(). To
that end let's use the new workqueue.

Let's atomically allocate small structures containing the required data
and fill it with information immediately upon being notified about the
change except for the pinctrl state which will be retrieved later from
process context. We can pretty reliably do this as pin functions are
typically set once per boot.

Let's make sure to bump the reference count of GPIO device and the GPIO
character device file descriptor to keep both alive until the event was
queued.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
 drivers/gpio/gpiolib-cdev.c | 82 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 71 insertions(+), 11 deletions(-)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index b242fdb1ad28..7759dca92f8b 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2333,6 +2333,7 @@ struct gpio_chardev_data {
 #ifdef CONFIG_GPIO_CDEV_V1
 	atomic_t watch_abi_version;
 #endif
+	struct file *fp;
 };
 
 static int chipinfo_get(struct gpio_chardev_data *cdev, void __user *ip)
@@ -2502,28 +2503,86 @@ static long gpio_ioctl_compat(struct file *file, unsigned int cmd,
 }
 #endif
 
+struct lineinfo_changed_ctx {
+	struct work_struct work;
+	struct gpio_v2_line_info_changed chg;
+	struct gpio_device *gdev;
+	struct gpio_chardev_data *cdev;
+};
+
+static void lineinfo_changed_func(struct work_struct *work)
+{
+	struct lineinfo_changed_ctx *ctx =
+			container_of(work, struct lineinfo_changed_ctx, work);
+	struct gpio_chip *gc;
+	int ret;
+
+	if (!(ctx->chg.info.flags & GPIO_V2_LINE_FLAG_USED)) {
+		/*
+		 * If nobody set the USED flag earlier, let's see with pinctrl
+		 * now. We're doing this late because it's a sleeping function.
+		 * Pin functions are in general much more static and while it's
+		 * not 100% bullet-proof, it's good enough for most cases.
+		 */
+		scoped_guard(srcu, &ctx->gdev->srcu) {
+			gc = srcu_dereference(ctx->gdev->chip, &ctx->gdev->srcu);
+			if (gc &&
+			    !pinctrl_gpio_can_use_line(gc, ctx->chg.info.offset))
+				ctx->chg.info.flags |= GPIO_V2_LINE_FLAG_USED;
+		}
+	}
+
+	ret = kfifo_in_spinlocked(&ctx->cdev->events, &ctx->chg, 1,
+				  &ctx->cdev->wait.lock);
+	if (ret)
+		wake_up_poll(&ctx->cdev->wait, EPOLLIN);
+	else
+		pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n");
+
+	gpio_device_put(ctx->gdev);
+	fput(ctx->cdev->fp);
+	kfree(ctx);
+}
+
 static int lineinfo_changed_notify(struct notifier_block *nb,
 				   unsigned long action, void *data)
 {
 	struct gpio_chardev_data *cdev =
 		container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb);
-	struct gpio_v2_line_info_changed chg;
+	struct lineinfo_changed_ctx *ctx;
 	struct gpio_desc *desc = data;
-	int ret;
 
 	if (!test_bit(gpio_chip_hwgpio(desc), cdev->watched_lines))
 		return NOTIFY_DONE;
 
-	memset(&chg, 0, sizeof(chg));
-	chg.event_type = action;
-	chg.timestamp_ns = ktime_get_ns();
-	gpio_desc_to_lineinfo(desc, &chg.info, false);
+	/*
+	 * If this is called from atomic context (for instance: with a spinlock
+	 * taken by the atomic notifier chain), any sleeping calls must be done
+	 * outside of this function in process context of the dedicated
+	 * workqueue.
+	 *
+	 * Let's gather as much info as possible from the descriptor and
+	 * postpone just the call to pinctrl_gpio_can_use_line() until the work
+	 * is executed.
+	 */
 
-	ret = kfifo_in_spinlocked(&cdev->events, &chg, 1, &cdev->wait.lock);
-	if (ret)
-		wake_up_poll(&cdev->wait, EPOLLIN);
-	else
-		pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n");
+	ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
+	if (!ctx) {
+		pr_err("Failed to allocate memory for line info notification\n");
+		return NOTIFY_DONE;
+	}
+
+	ctx->chg.event_type = action;
+	ctx->chg.timestamp_ns = ktime_get_ns();
+	gpio_desc_to_lineinfo(desc, &ctx->chg.info, true);
+	/* Keep the GPIO device alive until we emit the event. */
+	ctx->gdev = gpio_device_get(desc->gdev);
+	ctx->cdev = cdev;
+	/* Keep the file descriptor alive too. */
+	get_file(ctx->cdev->fp);
+
+	INIT_WORK(&ctx->work, lineinfo_changed_func);
+	queue_work(ctx->gdev->line_state_wq, &ctx->work);
 
 	return NOTIFY_OK;
 }
@@ -2683,6 +2742,7 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
 		goto out_unregister_line_notifier;
 
 	file->private_data = cdev;
+	cdev->fp = file;
 
 	ret = nonseekable_open(inode, file);
 	if (ret)

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 7/8] gpiolib: switch the line state notifier to atomic
  2024-10-17  8:14 [PATCH v4 0/8] gpio: notify user-space about config changes in the kernel Bartosz Golaszewski
                   ` (5 preceding siblings ...)
  2024-10-17  8:14 ` [PATCH v4 6/8] gpio: cdev: put emitting the line state events on a workqueue Bartosz Golaszewski
@ 2024-10-17  8:14 ` Bartosz Golaszewski
  2024-10-17  8:14 ` [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes Bartosz Golaszewski
  7 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17  8:14 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Kent Gibson
  Cc: linux-gpio, linux-kernel, Bartosz Golaszewski

From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

With everything else ready, we can now switch to using the atomic
notifier for line state events which will allow us to notify user-space
about direction changes from atomic context.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
 drivers/gpio/gpiolib-cdev.c | 12 ++++++------
 drivers/gpio/gpiolib.c      |  6 +++---
 drivers/gpio/gpiolib.h      |  2 +-
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 7759dca92f8b..cb4fb55e2696 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2729,8 +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 = blocking_notifier_chain_register(&gdev->line_state_notifier,
-					       &cdev->lineinfo_changed_nb);
+	ret = atomic_notifier_chain_register(&gdev->line_state_notifier,
+					     &cdev->lineinfo_changed_nb);
 	if (ret)
 		goto out_free_bitmap;
 
@@ -2754,8 +2754,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:
-	blocking_notifier_chain_unregister(&gdev->line_state_notifier,
-					   &cdev->lineinfo_changed_nb);
+	atomic_notifier_chain_unregister(&gdev->line_state_notifier,
+					 &cdev->lineinfo_changed_nb);
 out_free_bitmap:
 	gpio_device_put(gdev);
 	bitmap_free(cdev->watched_lines);
@@ -2779,8 +2779,8 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file)
 
 	blocking_notifier_chain_unregister(&gdev->device_notifier,
 					   &cdev->device_unregistered_nb);
-	blocking_notifier_chain_unregister(&gdev->line_state_notifier,
-					   &cdev->lineinfo_changed_nb);
+	atomic_notifier_chain_unregister(&gdev->line_state_notifier,
+					 &cdev->lineinfo_changed_nb);
 	bitmap_free(cdev->watched_lines);
 	gpio_device_put(gdev);
 	kfree(cdev);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 807bee86afd5..83e85dbfdeed 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1026,7 +1026,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
 		}
 	}
 
-	BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
+	ATOMIC_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
 	BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
 
 	ret = init_srcu_struct(&gdev->srcu);
@@ -4098,8 +4098,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);
 
 void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action)
 {
-	blocking_notifier_call_chain(&desc->gdev->line_state_notifier,
-				     action, desc);
+	atomic_notifier_call_chain(&desc->gdev->line_state_notifier,
+				   action, desc);
 }
 
 /**
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 8737e1641278..a54be597d21a 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -72,7 +72,7 @@ struct gpio_device {
 	const char		*label;
 	void			*data;
 	struct list_head        list;
-	struct blocking_notifier_head line_state_notifier;
+	struct atomic_notifier_head line_state_notifier;
 	struct workqueue_struct	*line_state_wq;
 	struct blocking_notifier_head device_notifier;
 	struct srcu_struct	srcu;

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes
  2024-10-17  8:14 [PATCH v4 0/8] gpio: notify user-space about config changes in the kernel Bartosz Golaszewski
                   ` (6 preceding siblings ...)
  2024-10-17  8:14 ` [PATCH v4 7/8] gpiolib: switch the line state notifier to atomic Bartosz Golaszewski
@ 2024-10-17  8:14 ` Bartosz Golaszewski
  2024-10-17 12:53   ` Kent Gibson
  7 siblings, 1 reply; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17  8:14 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Kent Gibson
  Cc: linux-gpio, linux-kernel, Bartosz Golaszewski

From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

We currently only notify user-space about line config changes that are
made from user-space. Any kernel config changes are not signalled.

Let's improve the situation by emitting the events closer to the source.
To that end let's call the relevant notifier chain from the functions
setting direction, gpiod_set_config(), gpiod_set_consumer_name() and
gpiod_toggle_active_low(). This covers all the options that we can
inform the user-space about. We ignore events which don't have
corresponding flags exported to user-space on purpose - otherwise the
user would see a config-changed event but the associated line-info would
remain unchanged.

gpiod_direction_output/input() can be called from any context.
Fortunately, we now emit line state events using an atomic notifier
chain, so it's no longer an issue.

Let's also add non-notifying wrappers around the direction setters in
order to not emit superfluous reconfigure events when requesting the
lines as the initial config should be part of the request notification.

Use gpio_do_set_config() instead of gpiod_set_debounce() for configuring
debouncing via hardware from the character device code to avoid multiple
reconfigure events.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
 drivers/gpio/gpiolib-cdev.c | 21 ++++++-----
 drivers/gpio/gpiolib.c      | 89 ++++++++++++++++++++++++++++++++++++++-------
 drivers/gpio/gpiolib.h      |  3 ++
 3 files changed, 90 insertions(+), 23 deletions(-)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index cb4fb55e2696..e5b15d96e952 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -195,8 +195,6 @@ static long linehandle_set_config(struct linehandle_state *lh,
 			if (ret)
 				return ret;
 		}
-
-		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
 	}
 	return 0;
 }
@@ -362,11 +360,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
 		if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
 			int val = !!handlereq.default_values[i];
 
-			ret = gpiod_direction_output(desc, val);
+			ret = gpiod_direction_output_nonotify(desc, val);
 			if (ret)
 				goto out_free_lh;
 		} else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
-			ret = gpiod_direction_input(desc);
+			ret = gpiod_direction_input_nonotify(desc);
 			if (ret)
 				goto out_free_lh;
 		}
@@ -922,8 +920,13 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us)
 	int ret, level, irq;
 	char *label;
 
-	/* try hardware */
-	ret = gpiod_set_debounce(line->desc, debounce_period_us);
+	/*
+	 * Try hardware. Skip gpiod_set_config() to avoid emitting two
+	 * CHANGED_CONFIG line state events.
+	 */
+	ret = gpio_do_set_config(line->desc,
+			pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE,
+						 debounce_period_us));
 	if (!ret) {
 		WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
 		return ret;
@@ -1447,8 +1450,6 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
 		}
 
 		WRITE_ONCE(line->edflags, edflags);
-
-		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
 	}
 	return 0;
 }
@@ -1700,11 +1701,11 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
 		if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
 			int val = gpio_v2_line_config_output_value(lc, i);
 
-			ret = gpiod_direction_output(desc, val);
+			ret = gpiod_direction_output_nonotify(desc, val);
 			if (ret)
 				goto out_free_linereq;
 		} else if (flags & GPIO_V2_LINE_FLAG_INPUT) {
-			ret = gpiod_direction_input(desc);
+			ret = gpiod_direction_input_nonotify(desc);
 			if (ret)
 				goto out_free_linereq;
 
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 83e85dbfdeed..ae758ba6dc3d 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2564,7 +2564,7 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
  * rely on gpio_request() having been called beforehand.
  */
 
-static int gpio_do_set_config(struct gpio_desc *desc, unsigned long config)
+int gpio_do_set_config(struct gpio_desc *desc, unsigned long config)
 {
 	int ret;
 
@@ -2670,9 +2670,15 @@ static int gpio_set_bias(struct gpio_desc *desc)
  */
 int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce)
 {
-	return gpio_set_config_with_argument_optional(desc,
-						      PIN_CONFIG_INPUT_DEBOUNCE,
-						      debounce);
+	int ret;
+
+	ret = gpio_set_config_with_argument_optional(desc,
+						     PIN_CONFIG_INPUT_DEBOUNCE,
+						     debounce);
+	if (!ret)
+		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
+
+	return ret;
 }
 
 /**
@@ -2686,6 +2692,18 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce)
  * 0 on success, or negative errno on failure.
  */
 int gpiod_direction_input(struct gpio_desc *desc)
+{
+	int ret;
+
+	ret = gpiod_direction_input_nonotify(desc);
+	if (ret == 0)
+		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(gpiod_direction_input);
+
+int gpiod_direction_input_nonotify(struct gpio_desc *desc)
 {
 	int ret = 0;
 
@@ -2733,7 +2751,6 @@ int gpiod_direction_input(struct gpio_desc *desc)
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(gpiod_direction_input);
 
 static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
 {
@@ -2795,8 +2812,15 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
  */
 int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
 {
+	int ret;
+
 	VALIDATE_DESC(desc);
-	return gpiod_direction_output_raw_commit(desc, value);
+
+	ret = gpiod_direction_output_raw_commit(desc, value);
+	if (ret == 0)
+		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
 
@@ -2814,6 +2838,18 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
  * 0 on success, or negative errno on failure.
  */
 int gpiod_direction_output(struct gpio_desc *desc, int value)
+{
+	int ret;
+
+	ret = gpiod_direction_output_nonotify(desc, value);
+	if (ret == 0)
+		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(gpiod_direction_output);
+
+int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value)
 {
 	unsigned long flags;
 	int ret;
@@ -2843,7 +2879,7 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
 			goto set_output_value;
 		/* Emulate open drain by not actively driving the line high */
 		if (value) {
-			ret = gpiod_direction_input(desc);
+			ret = gpiod_direction_input_nonotify(desc);
 			goto set_output_flag;
 		}
 	} else if (test_bit(FLAG_OPEN_SOURCE, &flags)) {
@@ -2852,7 +2888,7 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
 			goto set_output_value;
 		/* Emulate open source by not actively driving the line low */
 		if (!value) {
-			ret = gpiod_direction_input(desc);
+			ret = gpiod_direction_input_nonotify(desc);
 			goto set_output_flag;
 		}
 	} else {
@@ -2876,7 +2912,6 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
 		set_bit(FLAG_IS_OUT, &desc->flags);
 	return ret;
 }
-EXPORT_SYMBOL_GPL(gpiod_direction_output);
 
 /**
  * gpiod_enable_hw_timestamp_ns - Enable hardware timestamp in nanoseconds.
@@ -2955,9 +2990,30 @@ EXPORT_SYMBOL_GPL(gpiod_disable_hw_timestamp_ns);
  */
 int gpiod_set_config(struct gpio_desc *desc, unsigned long config)
 {
+	int ret;
+
 	VALIDATE_DESC(desc);
 
-	return gpio_do_set_config(desc, config);
+	ret = gpio_do_set_config(desc, config);
+	if (!ret) {
+		/* These are the only options we notify the userspace about. */
+		switch (pinconf_to_config_param(config)) {
+		case PIN_CONFIG_BIAS_DISABLE:
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+		case PIN_CONFIG_BIAS_PULL_UP:
+		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+		case PIN_CONFIG_DRIVE_PUSH_PULL:
+		case PIN_CONFIG_INPUT_DEBOUNCE:
+			gpiod_line_state_notify(desc,
+						GPIO_V2_LINE_CHANGED_CONFIG);
+			break;
+		default:
+			break;
+		}
+	}
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(gpiod_set_config);
 
@@ -3024,6 +3080,7 @@ void gpiod_toggle_active_low(struct gpio_desc *desc)
 {
 	VALIDATE_DESC_VOID(desc);
 	change_bit(FLAG_ACTIVE_LOW, &desc->flags);
+	gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
 }
 EXPORT_SYMBOL_GPL(gpiod_toggle_active_low);
 
@@ -3668,9 +3725,15 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep);
  */
 int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
 {
+	int ret;
+
 	VALIDATE_DESC(desc);
 
-	return desc_set_label(desc, name);
+	ret = desc_set_label(desc, name);
+	if (ret == 0)
+		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
 
@@ -4548,10 +4611,10 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
 
 	/* Process flags */
 	if (dflags & GPIOD_FLAGS_BIT_DIR_OUT)
-		ret = gpiod_direction_output(desc,
+		ret = gpiod_direction_output_nonotify(desc,
 				!!(dflags & GPIOD_FLAGS_BIT_DIR_VAL));
 	else
-		ret = gpiod_direction_input(desc);
+		ret = gpiod_direction_input_nonotify(desc);
 
 	return ret;
 }
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index a54be597d21a..83690f72f7e5 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -155,6 +155,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
 int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
 
 void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
+int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value);
+int gpiod_direction_input_nonotify(struct gpio_desc *desc);
 
 struct gpio_desc_label {
 	struct rcu_head rh;
@@ -258,6 +260,7 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
 					 const char *label,
 					 bool platform_lookup_allowed);
 
+int gpio_do_set_config(struct gpio_desc *desc, unsigned long config);
 int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
 		unsigned long lflags, enum gpiod_flags dflags);
 int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce);

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 3/8] gpio: cdev: go back to storing debounce period in the GPIO descriptor
  2024-10-17  8:14 ` [PATCH v4 3/8] gpio: cdev: go back to storing debounce period in the GPIO descriptor Bartosz Golaszewski
@ 2024-10-17 12:44   ` Kent Gibson
  2024-10-17 14:13     ` Bartosz Golaszewski
  0 siblings, 1 reply; 21+ messages in thread
From: Kent Gibson @ 2024-10-17 12:44 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 10:14:11AM +0200, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> @@ -1047,7 +925,7 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us)
>  	/* try hardware */
>  	ret = gpiod_set_debounce(line->desc, debounce_period_us);
>  	if (!ret) {
> -		line_set_debounce_period(line, debounce_period_us);
> +		WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
>  		return ret;
>  	}

Not related to this change, but this check looks redundant to me - the same
is performed where debounce_setup() is called.

Want a patch to remove it?

Cheers,
Kent.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes
  2024-10-17  8:14 ` [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes Bartosz Golaszewski
@ 2024-10-17 12:53   ` Kent Gibson
  2024-10-17 14:14     ` Bartosz Golaszewski
  0 siblings, 1 reply; 21+ messages in thread
From: Kent Gibson @ 2024-10-17 12:53 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 10:14:16AM +0200, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> @@ -1447,8 +1450,6 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
>  		}
>
>  		WRITE_ONCE(line->edflags, edflags);
> -
> -		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
>  	}
>  	return 0;
>  }

I still get errors from this when reconfiguring lines with debounce.
You should leave this notify in place and use _nonotify when setting the
direction.
i.e.

@@ -1436,11 +1432,11 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
                        int val = gpio_v2_line_config_output_value(&lc, i);

                        edge_detector_stop(line);
-                       ret = gpiod_direction_output(desc, val);
+                       ret = gpiod_direction_output_nonotify(desc, val);
                        if (ret)
                                return ret;
                } else {
-                       ret = gpiod_direction_input(desc);
+                       ret = gpiod_direction_input_nonotify(desc);
                        if (ret)
                                return ret;

@@ -1450,6 +1446,8 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
                }

                WRITE_ONCE(line->edflags, edflags);
+
+               gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
        }
        return 0;
 }

Given that, all my current tests are passing.

Cheers,
Kent.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 3/8] gpio: cdev: go back to storing debounce period in the GPIO descriptor
  2024-10-17 12:44   ` Kent Gibson
@ 2024-10-17 14:13     ` Bartosz Golaszewski
  2024-10-17 14:16       ` Kent Gibson
  0 siblings, 1 reply; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17 14:13 UTC (permalink / raw)
  To: Kent Gibson; +Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 2:44 PM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Thu, Oct 17, 2024 at 10:14:11AM +0200, Bartosz Golaszewski wrote:
> > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> >
> > @@ -1047,7 +925,7 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us)
> >       /* try hardware */
> >       ret = gpiod_set_debounce(line->desc, debounce_period_us);
> >       if (!ret) {
> > -             line_set_debounce_period(line, debounce_period_us);
> > +             WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
> >               return ret;
> >       }
>
> Not related to this change, but this check looks redundant to me - the same
> is performed where debounce_setup() is called.
>
> Want a patch to remove it?
>
> Cheers,
> Kent.

Sure! Can you rebase it on top of my series?

Bart

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes
  2024-10-17 12:53   ` Kent Gibson
@ 2024-10-17 14:14     ` Bartosz Golaszewski
  2024-10-17 14:20       ` Kent Gibson
  0 siblings, 1 reply; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17 14:14 UTC (permalink / raw)
  To: Kent Gibson; +Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 2:53 PM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Thu, Oct 17, 2024 at 10:14:16AM +0200, Bartosz Golaszewski wrote:
> > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> >
> > @@ -1447,8 +1450,6 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> >               }
> >
> >               WRITE_ONCE(line->edflags, edflags);
> > -
> > -             gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> >       }
> >       return 0;
> >  }
>
> I still get errors from this when reconfiguring lines with debounce.
> You should leave this notify in place and use _nonotify when setting the
> direction.
> i.e.
>
> @@ -1436,11 +1432,11 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
>                         int val = gpio_v2_line_config_output_value(&lc, i);
>
>                         edge_detector_stop(line);
> -                       ret = gpiod_direction_output(desc, val);
> +                       ret = gpiod_direction_output_nonotify(desc, val);
>                         if (ret)
>                                 return ret;
>                 } else {
> -                       ret = gpiod_direction_input(desc);
> +                       ret = gpiod_direction_input_nonotify(desc);
>                         if (ret)
>                                 return ret;
>
> @@ -1450,6 +1446,8 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
>                 }
>
>                 WRITE_ONCE(line->edflags, edflags);
> +
> +               gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
>         }
>         return 0;
>  }
>
> Given that, all my current tests are passing.
>

That looks good - after all we no longer notify from any place in
gpiolib-cdev.c anymore - but I'd like to learn what's wrong exactly.
Are you getting more events with debounce? Are you not getting any?

Bart

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 3/8] gpio: cdev: go back to storing debounce period in the GPIO descriptor
  2024-10-17 14:13     ` Bartosz Golaszewski
@ 2024-10-17 14:16       ` Kent Gibson
  2024-10-17 14:43         ` Bartosz Golaszewski
  0 siblings, 1 reply; 21+ messages in thread
From: Kent Gibson @ 2024-10-17 14:16 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 04:13:14PM +0200, Bartosz Golaszewski wrote:
> On Thu, Oct 17, 2024 at 2:44 PM Kent Gibson <warthog618@gmail.com> wrote:
> >
> > On Thu, Oct 17, 2024 at 10:14:11AM +0200, Bartosz Golaszewski wrote:
> > > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> > >
> > > @@ -1047,7 +925,7 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us)
> > >       /* try hardware */
> > >       ret = gpiod_set_debounce(line->desc, debounce_period_us);
> > >       if (!ret) {
> > > -             line_set_debounce_period(line, debounce_period_us);
> > > +             WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
> > >               return ret;
> > >       }
> >
> > Not related to this change, but this check looks redundant to me - the same
> > is performed where debounce_setup() is called.
> >
> > Want a patch to remove it?
> >
> > Cheers,
> > Kent.
>
> Sure! Can you rebase it on top of my series?
>

Will do - once patch 8 is sorted - so v5?

Cheers,
Kent.


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes
  2024-10-17 14:14     ` Bartosz Golaszewski
@ 2024-10-17 14:20       ` Kent Gibson
  2024-10-17 14:25         ` Kent Gibson
  2024-10-17 14:59         ` Bartosz Golaszewski
  0 siblings, 2 replies; 21+ messages in thread
From: Kent Gibson @ 2024-10-17 14:20 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 04:14:24PM +0200, Bartosz Golaszewski wrote:
> On Thu, Oct 17, 2024 at 2:53 PM Kent Gibson <warthog618@gmail.com> wrote:
> >
> > On Thu, Oct 17, 2024 at 10:14:16AM +0200, Bartosz Golaszewski wrote:
> > > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> > >
> > > @@ -1447,8 +1450,6 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > >               }
> > >
> > >               WRITE_ONCE(line->edflags, edflags);
> > > -
> > > -             gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> > >       }
> > >       return 0;
> > >  }
> >
> > I still get errors from this when reconfiguring lines with debounce.
> > You should leave this notify in place and use _nonotify when setting the
> > direction.
> > i.e.
> >
> > @@ -1436,11 +1432,11 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> >                         int val = gpio_v2_line_config_output_value(&lc, i);
> >
> >                         edge_detector_stop(line);
> > -                       ret = gpiod_direction_output(desc, val);
> > +                       ret = gpiod_direction_output_nonotify(desc, val);
> >                         if (ret)
> >                                 return ret;
> >                 } else {
> > -                       ret = gpiod_direction_input(desc);
> > +                       ret = gpiod_direction_input_nonotify(desc);
> >                         if (ret)
> >                                 return ret;
> >
> > @@ -1450,6 +1446,8 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> >                 }
> >
> >                 WRITE_ONCE(line->edflags, edflags);
> > +
> > +               gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> >         }
> >         return 0;
> >  }
> >
> > Given that, all my current tests are passing.
> >
>
> That looks good - after all we no longer notify from any place in
> gpiolib-cdev.c anymore - but I'd like to learn what's wrong exactly.
> Are you getting more events with debounce? Are you not getting any?
>

In linereq_set_config(), the notify comes from the gpiod_direction_input() -
before the edge_detector_setup() is called (not visible in the patch) and that
sets the debounce value in the desc.
So you get an event without the debounce set, or with a stale value.
Keeping the gpiod_line_state_notify() and using the _nonotify()
functions means the notify comes after the debounce has been set.

Cheers,
Kent.


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes
  2024-10-17 14:20       ` Kent Gibson
@ 2024-10-17 14:25         ` Kent Gibson
  2024-10-17 14:59         ` Bartosz Golaszewski
  1 sibling, 0 replies; 21+ messages in thread
From: Kent Gibson @ 2024-10-17 14:25 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 10:20:53PM +0800, Kent Gibson wrote:
> On Thu, Oct 17, 2024 at 04:14:24PM +0200, Bartosz Golaszewski wrote:
> > On Thu, Oct 17, 2024 at 2:53 PM Kent Gibson <warthog618@gmail.com> wrote:
> > >
> > > On Thu, Oct 17, 2024 at 10:14:16AM +0200, Bartosz Golaszewski wrote:
> > > > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> > > >
> > > > @@ -1447,8 +1450,6 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > > >               }
> > > >
> > > >               WRITE_ONCE(line->edflags, edflags);
> > > > -
> > > > -             gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> > > >       }
> > > >       return 0;
> > > >  }
> > >
> > > I still get errors from this when reconfiguring lines with debounce.
> > > You should leave this notify in place and use _nonotify when setting the
> > > direction.
> > > i.e.
> > >
> > > @@ -1436,11 +1432,11 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > >                         int val = gpio_v2_line_config_output_value(&lc, i);
> > >
> > >                         edge_detector_stop(line);
> > > -                       ret = gpiod_direction_output(desc, val);
> > > +                       ret = gpiod_direction_output_nonotify(desc, val);
> > >                         if (ret)
> > >                                 return ret;
> > >                 } else {
> > > -                       ret = gpiod_direction_input(desc);
> > > +                       ret = gpiod_direction_input_nonotify(desc);
> > >                         if (ret)
> > >                                 return ret;
> > >
> > > @@ -1450,6 +1446,8 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > >                 }
> > >
> > >                 WRITE_ONCE(line->edflags, edflags);
> > > +
> > > +               gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> > >         }
> > >         return 0;
> > >  }
> > >
> > > Given that, all my current tests are passing.
> > >
> >
> > That looks good - after all we no longer notify from any place in
> > gpiolib-cdev.c anymore - but I'd like to learn what's wrong exactly.
> > Are you getting more events with debounce? Are you not getting any?
> >
>
> In linereq_set_config(), the notify comes from the gpiod_direction_input() -
> before the edge_detector_setup() is called (not visible in the patch) and that
> sets the debounce value in the desc.

That should be edge_detector_update().

> So you get an event without the debounce set, or with a stale value.
> Keeping the gpiod_line_state_notify() and using the _nonotify()
> functions means the notify comes after the debounce has been set.
>
> Cheers,
> Kent.
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 3/8] gpio: cdev: go back to storing debounce period in the GPIO descriptor
  2024-10-17 14:16       ` Kent Gibson
@ 2024-10-17 14:43         ` Bartosz Golaszewski
  0 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17 14:43 UTC (permalink / raw)
  To: Kent Gibson; +Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 4:16 PM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Thu, Oct 17, 2024 at 04:13:14PM +0200, Bartosz Golaszewski wrote:
> > On Thu, Oct 17, 2024 at 2:44 PM Kent Gibson <warthog618@gmail.com> wrote:
> > >
> > > On Thu, Oct 17, 2024 at 10:14:11AM +0200, Bartosz Golaszewski wrote:
> > > > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> > > >
> > > > @@ -1047,7 +925,7 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us)
> > > >       /* try hardware */
> > > >       ret = gpiod_set_debounce(line->desc, debounce_period_us);
> > > >       if (!ret) {
> > > > -             line_set_debounce_period(line, debounce_period_us);
> > > > +             WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
> > > >               return ret;
> > > >       }
> > >
> > > Not related to this change, but this check looks redundant to me - the same
> > > is performed where debounce_setup() is called.
> > >
> > > Want a patch to remove it?
> > >
> > > Cheers,
> > > Kent.
> >
> > Sure! Can you rebase it on top of my series?
> >
>
> Will do - once patch 8 is sorted - so v5?

I will send it out tomorrow.

Bart

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes
  2024-10-17 14:20       ` Kent Gibson
  2024-10-17 14:25         ` Kent Gibson
@ 2024-10-17 14:59         ` Bartosz Golaszewski
  2024-10-17 15:02           ` Kent Gibson
  1 sibling, 1 reply; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17 14:59 UTC (permalink / raw)
  To: Kent Gibson; +Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 4:20 PM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Thu, Oct 17, 2024 at 04:14:24PM +0200, Bartosz Golaszewski wrote:
> > On Thu, Oct 17, 2024 at 2:53 PM Kent Gibson <warthog618@gmail.com> wrote:
> > >
> > > On Thu, Oct 17, 2024 at 10:14:16AM +0200, Bartosz Golaszewski wrote:
> > > > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> > > >
> > > > @@ -1447,8 +1450,6 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > > >               }
> > > >
> > > >               WRITE_ONCE(line->edflags, edflags);
> > > > -
> > > > -             gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> > > >       }
> > > >       return 0;
> > > >  }
> > >
> > > I still get errors from this when reconfiguring lines with debounce.
> > > You should leave this notify in place and use _nonotify when setting the
> > > direction.
> > > i.e.
> > >
> > > @@ -1436,11 +1432,11 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > >                         int val = gpio_v2_line_config_output_value(&lc, i);
> > >
> > >                         edge_detector_stop(line);
> > > -                       ret = gpiod_direction_output(desc, val);
> > > +                       ret = gpiod_direction_output_nonotify(desc, val);
> > >                         if (ret)
> > >                                 return ret;
> > >                 } else {
> > > -                       ret = gpiod_direction_input(desc);
> > > +                       ret = gpiod_direction_input_nonotify(desc);
> > >                         if (ret)
> > >                                 return ret;
> > >
> > > @@ -1450,6 +1446,8 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > >                 }
> > >
> > >                 WRITE_ONCE(line->edflags, edflags);
> > > +
> > > +               gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> > >         }
> > >         return 0;
> > >  }
> > >
> > > Given that, all my current tests are passing.
> > >
> >
> > That looks good - after all we no longer notify from any place in
> > gpiolib-cdev.c anymore - but I'd like to learn what's wrong exactly.
> > Are you getting more events with debounce? Are you not getting any?
> >
>
> In linereq_set_config(), the notify comes from the gpiod_direction_input() -
> before the edge_detector_setup() is called (not visible in the patch) and that
> sets the debounce value in the desc.
> So you get an event without the debounce set, or with a stale value.
> Keeping the gpiod_line_state_notify() and using the _nonotify()
> functions means the notify comes after the debounce has been set.
>

I see. I guess I should do the same both for linehandle_set_config()
and linereq_set_config()?

Bart

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes
  2024-10-17 14:59         ` Bartosz Golaszewski
@ 2024-10-17 15:02           ` Kent Gibson
  2024-10-17 15:04             ` Bartosz Golaszewski
  0 siblings, 1 reply; 21+ messages in thread
From: Kent Gibson @ 2024-10-17 15:02 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 04:59:46PM +0200, Bartosz Golaszewski wrote:
> On Thu, Oct 17, 2024 at 4:20 PM Kent Gibson <warthog618@gmail.com> wrote:
> >
> > On Thu, Oct 17, 2024 at 04:14:24PM +0200, Bartosz Golaszewski wrote:
> > > On Thu, Oct 17, 2024 at 2:53 PM Kent Gibson <warthog618@gmail.com> wrote:
> > > >
> > > > On Thu, Oct 17, 2024 at 10:14:16AM +0200, Bartosz Golaszewski wrote:
> > > > > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> > > > >
> > > > > @@ -1447,8 +1450,6 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > > > >               }
> > > > >
> > > > >               WRITE_ONCE(line->edflags, edflags);
> > > > > -
> > > > > -             gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> > > > >       }
> > > > >       return 0;
> > > > >  }
> > > >
> > > > I still get errors from this when reconfiguring lines with debounce.
> > > > You should leave this notify in place and use _nonotify when setting the
> > > > direction.
> > > > i.e.
> > > >
> > > > @@ -1436,11 +1432,11 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > > >                         int val = gpio_v2_line_config_output_value(&lc, i);
> > > >
> > > >                         edge_detector_stop(line);
> > > > -                       ret = gpiod_direction_output(desc, val);
> > > > +                       ret = gpiod_direction_output_nonotify(desc, val);
> > > >                         if (ret)
> > > >                                 return ret;
> > > >                 } else {
> > > > -                       ret = gpiod_direction_input(desc);
> > > > +                       ret = gpiod_direction_input_nonotify(desc);
> > > >                         if (ret)
> > > >                                 return ret;
> > > >
> > > > @@ -1450,6 +1446,8 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > > >                 }
> > > >
> > > >                 WRITE_ONCE(line->edflags, edflags);
> > > > +
> > > > +               gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> > > >         }
> > > >         return 0;
> > > >  }
> > > >
> > > > Given that, all my current tests are passing.
> > > >
> > >
> > > That looks good - after all we no longer notify from any place in
> > > gpiolib-cdev.c anymore - but I'd like to learn what's wrong exactly.
> > > Are you getting more events with debounce? Are you not getting any?
> > >
> >
> > In linereq_set_config(), the notify comes from the gpiod_direction_input() -
> > before the edge_detector_setup() is called (not visible in the patch) and that
> > sets the debounce value in the desc.
> > So you get an event without the debounce set, or with a stale value.
> > Keeping the gpiod_line_state_notify() and using the _nonotify()
> > functions means the notify comes after the debounce has been set.
> >
>
> I see. I guess I should do the same both for linehandle_set_config()
> and linereq_set_config()?
>

linehandles don't support debounce, so it's good as is.

Cheers,
Kent.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes
  2024-10-17 15:02           ` Kent Gibson
@ 2024-10-17 15:04             ` Bartosz Golaszewski
  2024-10-17 15:12               ` Kent Gibson
  0 siblings, 1 reply; 21+ messages in thread
From: Bartosz Golaszewski @ 2024-10-17 15:04 UTC (permalink / raw)
  To: Kent Gibson; +Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 5:02 PM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Thu, Oct 17, 2024 at 04:59:46PM +0200, Bartosz Golaszewski wrote:
> > On Thu, Oct 17, 2024 at 4:20 PM Kent Gibson <warthog618@gmail.com> wrote:
> > >
> > > On Thu, Oct 17, 2024 at 04:14:24PM +0200, Bartosz Golaszewski wrote:
> > > > On Thu, Oct 17, 2024 at 2:53 PM Kent Gibson <warthog618@gmail.com> wrote:
> > > > >
> > > > > On Thu, Oct 17, 2024 at 10:14:16AM +0200, Bartosz Golaszewski wrote:
> > > > > > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> > > > > >
> > > > > > @@ -1447,8 +1450,6 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > > > > >               }
> > > > > >
> > > > > >               WRITE_ONCE(line->edflags, edflags);
> > > > > > -
> > > > > > -             gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> > > > > >       }
> > > > > >       return 0;
> > > > > >  }
> > > > >
> > > > > I still get errors from this when reconfiguring lines with debounce.
> > > > > You should leave this notify in place and use _nonotify when setting the
> > > > > direction.
> > > > > i.e.
> > > > >
> > > > > @@ -1436,11 +1432,11 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > > > >                         int val = gpio_v2_line_config_output_value(&lc, i);
> > > > >
> > > > >                         edge_detector_stop(line);
> > > > > -                       ret = gpiod_direction_output(desc, val);
> > > > > +                       ret = gpiod_direction_output_nonotify(desc, val);
> > > > >                         if (ret)
> > > > >                                 return ret;
> > > > >                 } else {
> > > > > -                       ret = gpiod_direction_input(desc);
> > > > > +                       ret = gpiod_direction_input_nonotify(desc);
> > > > >                         if (ret)
> > > > >                                 return ret;
> > > > >
> > > > > @@ -1450,6 +1446,8 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > > > >                 }
> > > > >
> > > > >                 WRITE_ONCE(line->edflags, edflags);
> > > > > +
> > > > > +               gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> > > > >         }
> > > > >         return 0;
> > > > >  }
> > > > >
> > > > > Given that, all my current tests are passing.
> > > > >
> > > >
> > > > That looks good - after all we no longer notify from any place in
> > > > gpiolib-cdev.c anymore - but I'd like to learn what's wrong exactly.
> > > > Are you getting more events with debounce? Are you not getting any?
> > > >
> > >
> > > In linereq_set_config(), the notify comes from the gpiod_direction_input() -
> > > before the edge_detector_setup() is called (not visible in the patch) and that
> > > sets the debounce value in the desc.
> > > So you get an event without the debounce set, or with a stale value.
> > > Keeping the gpiod_line_state_notify() and using the _nonotify()
> > > functions means the notify comes after the debounce has been set.
> > >
> >
> > I see. I guess I should do the same both for linehandle_set_config()
> > and linereq_set_config()?
> >
>
> linehandles don't support debounce, so it's good as is.
>

Right, but I'm wondering if it isn't better for consistency.
Otherwise, someone may ask themselves why there's no event being
emitted if they're not familiar with the gpiod_direction_*()
internals.

Bart

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes
  2024-10-17 15:04             ` Bartosz Golaszewski
@ 2024-10-17 15:12               ` Kent Gibson
  0 siblings, 0 replies; 21+ messages in thread
From: Kent Gibson @ 2024-10-17 15:12 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski

On Thu, Oct 17, 2024 at 05:04:13PM +0200, Bartosz Golaszewski wrote:
> On Thu, Oct 17, 2024 at 5:02 PM Kent Gibson <warthog618@gmail.com> wrote:
> >
> > On Thu, Oct 17, 2024 at 04:59:46PM +0200, Bartosz Golaszewski wrote:
> > > On Thu, Oct 17, 2024 at 4:20 PM Kent Gibson <warthog618@gmail.com> wrote:
> > > >
> > > > On Thu, Oct 17, 2024 at 04:14:24PM +0200, Bartosz Golaszewski wrote:
> > > > > On Thu, Oct 17, 2024 at 2:53 PM Kent Gibson <warthog618@gmail.com> wrote:
> > > > > >
> > > > > > On Thu, Oct 17, 2024 at 10:14:16AM +0200, Bartosz Golaszewski wrote:
> > > > > > > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> > > > > > >
> > > > > > > @@ -1447,8 +1450,6 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > > > > > >               }
> > > > > > >
> > > > > > >               WRITE_ONCE(line->edflags, edflags);
> > > > > > > -
> > > > > > > -             gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> > > > > > >       }
> > > > > > >       return 0;
> > > > > > >  }
> > > > > >
> > > > > > I still get errors from this when reconfiguring lines with debounce.
> > > > > > You should leave this notify in place and use _nonotify when setting the
> > > > > > direction.
> > > > > > i.e.
> > > > > >
> > > > > > @@ -1436,11 +1432,11 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > > > > >                         int val = gpio_v2_line_config_output_value(&lc, i);
> > > > > >
> > > > > >                         edge_detector_stop(line);
> > > > > > -                       ret = gpiod_direction_output(desc, val);
> > > > > > +                       ret = gpiod_direction_output_nonotify(desc, val);
> > > > > >                         if (ret)
> > > > > >                                 return ret;
> > > > > >                 } else {
> > > > > > -                       ret = gpiod_direction_input(desc);
> > > > > > +                       ret = gpiod_direction_input_nonotify(desc);
> > > > > >                         if (ret)
> > > > > >                                 return ret;
> > > > > >
> > > > > > @@ -1450,6 +1446,8 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
> > > > > >                 }
> > > > > >
> > > > > >                 WRITE_ONCE(line->edflags, edflags);
> > > > > > +
> > > > > > +               gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
> > > > > >         }
> > > > > >         return 0;
> > > > > >  }
> > > > > >
> > > > > > Given that, all my current tests are passing.
> > > > > >
> > > > >
> > > > > That looks good - after all we no longer notify from any place in
> > > > > gpiolib-cdev.c anymore - but I'd like to learn what's wrong exactly.
> > > > > Are you getting more events with debounce? Are you not getting any?
> > > > >
> > > >
> > > > In linereq_set_config(), the notify comes from the gpiod_direction_input() -
> > > > before the edge_detector_setup() is called (not visible in the patch) and that
> > > > sets the debounce value in the desc.
> > > > So you get an event without the debounce set, or with a stale value.
> > > > Keeping the gpiod_line_state_notify() and using the _nonotify()
> > > > functions means the notify comes after the debounce has been set.
> > > >
> > >
> > > I see. I guess I should do the same both for linehandle_set_config()
> > > and linereq_set_config()?
> > >
> >
> > linehandles don't support debounce, so it's good as is.
> >
>
> Right, but I'm wondering if it isn't better for consistency.
> Otherwise, someone may ask themselves why there's no event being
> emitted if they're not familiar with the gpiod_direction_*()
> internals.
>

I prefer the more succinct form myself, and if you are working in the
kernel you should be capable of determining what the functions do, but
whichever works for you.

Cheers,
Kent.


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2024-10-17 15:12 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-17  8:14 [PATCH v4 0/8] gpio: notify user-space about config changes in the kernel Bartosz Golaszewski
2024-10-17  8:14 ` [PATCH v4 1/8] gpiolib: notify user-space when a driver requests its own desc Bartosz Golaszewski
2024-10-17  8:14 ` [PATCH v4 2/8] gpiolib: unduplicate chip guard in set_config path Bartosz Golaszewski
2024-10-17  8:14 ` [PATCH v4 3/8] gpio: cdev: go back to storing debounce period in the GPIO descriptor Bartosz Golaszewski
2024-10-17 12:44   ` Kent Gibson
2024-10-17 14:13     ` Bartosz Golaszewski
2024-10-17 14:16       ` Kent Gibson
2024-10-17 14:43         ` Bartosz Golaszewski
2024-10-17  8:14 ` [PATCH v4 4/8] gpio: cdev: prepare gpio_desc_to_lineinfo() for being called from atomic Bartosz Golaszewski
2024-10-17  8:14 ` [PATCH v4 5/8] gpiolib: add a per-gpio_device line state notification workqueue Bartosz Golaszewski
2024-10-17  8:14 ` [PATCH v4 6/8] gpio: cdev: put emitting the line state events on a workqueue Bartosz Golaszewski
2024-10-17  8:14 ` [PATCH v4 7/8] gpiolib: switch the line state notifier to atomic Bartosz Golaszewski
2024-10-17  8:14 ` [PATCH v4 8/8] gpiolib: notify user-space about in-kernel line state changes Bartosz Golaszewski
2024-10-17 12:53   ` Kent Gibson
2024-10-17 14:14     ` Bartosz Golaszewski
2024-10-17 14:20       ` Kent Gibson
2024-10-17 14:25         ` Kent Gibson
2024-10-17 14:59         ` Bartosz Golaszewski
2024-10-17 15:02           ` Kent Gibson
2024-10-17 15:04             ` Bartosz Golaszewski
2024-10-17 15:12               ` Kent Gibson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).