[PATCH 3/5] gpiolib: provide gpiod_update_flags()
From: Bartosz Golaszewski
Date: Wed Nov 04 2026 - 10:51:52 EST
We have several places in GPIO core where we copy the desc->flags field,
change several bits in the copy and write back the whole word with
WRITE_ONCE(). Concurrent updates of flags that modify other bits are
lost.
Add gpiod_update_flags() - it replaces a masked set of bits with
try_cmpxchg() loop. All bits are updated in one atomic step so no other
thread can observe it half-done. Unmasked bits are not rewritten with
stale values.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxxxxxxxx>
---
drivers/gpio/gpiolib.h | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 650a702741dfb938fbdf11db65a46ac85b6af512..10659ffcf2baef9d4a7584858cda1849b26c12e8 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -9,6 +9,7 @@
#ifndef GPIOLIB_H
#define GPIOLIB_H
+#include <linux/atomic.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -223,6 +224,26 @@ struct gpio_desc {
#endif
};
+/**
+ * gpiod_update_flags() - Atomically update several descriptor flags at once
+ * @desc: GPIO descriptor to update.
+ * @mask: Bits (not bit numbers) to modify.
+ * @val: New values for the bits selected by @mask.
+ *
+ * Replace the bits in @mask with the corresponding bits of @val in a single
+ * atomic step, so that no other context can observe a half-updated state of
+ * these bits. Bits outside of @mask are never rewritten with stale values, so
+ * concurrent atomic bit operations on them are preserved.
+ */
+static inline void gpiod_update_flags(struct gpio_desc *desc,
+ unsigned long mask, unsigned long val)
+{
+ unsigned long old = READ_ONCE(desc->flags);
+
+ while (!try_cmpxchg(&desc->flags, &old, (old & ~mask) | (val & mask)))
+ ;
+}
+
#define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)
struct gpio_chip_guard {
--
2.47.3