mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/7] regmap: cleanups for regcache and regmap init paths
@ 2026-09-30  9:46 Peng Fan (OSS)
  2026-09-30  9:46 ` [PATCH 1/7] regcache: extract locked helpers to replace open-coded lock/unlock pairs Peng Fan (OSS)
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30  9:46 UTC (permalink / raw)
  To: Mark Brown, Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: linux-kernel, driver-core, Peng Fan

Following up the previous regmap guard lock patchset [1].

[1] https://lore.kernel.org/all/179062150526.139547.3870545428656485907.b4-ty@b4/#r

This is the 2nd batch to do more cleanup.

This series tidies up locking, error handling, and resource management
across the regmap core and regcache.

- Extract regcache_locked_op() and regcache_locked_exit() helpers to
  replace open-coded lock/unlock pairs in regcache_init() and
  regcache_exit(), avoiding the cleanup.h-vs-goto concern that
  prevented the earlier scoped lock guard conversion.

- Extract __regcache_sync() to deduplicate the sync dispatch logic
  shared between regcache_sync() and regcache_sync_region().

- Flatten control flow in regcache_init(), regcache_lookup_reg(), and
  regcache_sync_block() by using early returns, moving guards into
  helpers, and merging redundant sanity checks.

- Switch regmap_register_patch() from krealloc() to krealloc_array()
  for overflow-safe element-count arithmetic.

- Convert bus struct copies in regmap-i2c and regmap-spi from
  kmemdup() to devm_kmemdup(), letting devres handle lifetime and
  removing the free_on_exit flag from struct regmap_bus entirely.

- Simplify the early error paths in __regmap_init() by returning
  directly instead of jumping to a label that does nothing but return.

- NULL map->reg_defaults after freeing in regcache_exit() to prevent
  a potential double-free if regmap_reinit_cache() later fails during
  cache re-initialisation.

Tested with the regmap KUnit suite (drivers/base/regmap/regmap-kunit.c)
with lockdep (PROVE_LOCKING, DEBUG_LOCK_ALLOC, DEBUG_ATOMIC_SLEEP) enabled.
Built clean with sparse (C=1).

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
Peng Fan (7):
      regcache: extract locked helpers to replace open-coded lock/unlock pairs
      regcache: extract __regcache_sync() to deduplicate sync dispatch
      regcache: simplify control flow and reduce nesting
      regmap: use krealloc_array() in regmap_register_patch()
      regmap: use devm_kmemdup() for bus copies and remove free_on_exit
      regmap: return errors directly before map allocation in __regmap_init()
      regmap: fix potential double-free of map->reg_defaults on cache reinit

 drivers/base/regmap/regcache.c   | 132 +++++++++++++++++++--------------------
 drivers/base/regmap/regmap-i2c.c |   3 +-
 drivers/base/regmap/regmap-spi.c |   3 +-
 drivers/base/regmap/regmap.c     |  31 +++------
 include/linux/regmap.h           |   2 -
 5 files changed, 77 insertions(+), 94 deletions(-)
---
base-commit: 6474fa070f2b8013b4b87350b775b8c3be6e8aac
change-id: 20260930-regmap-lock-2nd-f481885c24d6

Best regards,
--  
Peng Fan <peng.fan@nxp.com>


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

* [PATCH 1/7] regcache: extract locked helpers to replace open-coded lock/unlock pairs
  2026-09-30  9:46 [PATCH 0/7] regmap: cleanups for regcache and regmap init paths Peng Fan (OSS)
@ 2026-09-30  9:46 ` Peng Fan (OSS)
  2026-09-30  9:46 ` [PATCH 2/7] regcache: extract __regcache_sync() to deduplicate sync dispatch Peng Fan (OSS)
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30  9:46 UTC (permalink / raw)
  To: Mark Brown, Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: linux-kernel, driver-core, Peng Fan

From: Peng Fan <peng.fan@nxp.com>

The previous patch ("regcache: use the regmap scoped lock guard")
deliberately left regcache_init() with explicit map->lock()/unlock()
calls because the function has a goto err_* cleanup ladder and mixing
goto with cleanup helpers in the same function is discouraged by
cleanup.h.

Solve this by extracting two static helpers:

  regcache_locked_op()  - acquires the regmap lock via guard(regmap),
                          invokes an int-returning cache_ops callback
                          (init, populate), and returns its result.
                          Returns 0 when the callback is NULL.

  regcache_locked_exit() - acquires the regmap lock via guard(regmap)
                           and invokes the void-returning exit
                           callback.  Returns immediately when the
                           callback is NULL.

Because the lock scope is now entirely inside the helper functions,
it never crosses a goto target in the caller, so the cleanup.h
concern does not apply.

Convert all four remaining open-coded lock/unlock sites in
regcache_init() (init, populate, error-path exit) and the
scoped_guard in regcache_exit() to use these helpers.

No functional change.

Assisted-by: LLM
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/base/regmap/regcache.c | 57 +++++++++++++++++++++---------------------
 1 file changed, 29 insertions(+), 28 deletions(-)

diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 4c80db5d6f6f..ebfa27bb6f54 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -121,6 +121,28 @@ static void regcache_hw_exit(struct regmap *map)
 		kfree(map->reg_defaults_raw);
 }
 
+static int regcache_locked_op(struct regmap *map,
+			      int (*op)(struct regmap *map),
+			      const char *action)
+{
+	if (!op)
+		return 0;
+
+	dev_dbg(map->dev, "%s %s cache\n", action, map->cache_ops->name);
+	guard(regmap)(map);
+	return op(map);
+}
+
+static void regcache_locked_exit(struct regmap *map)
+{
+	if (!map->cache_ops->exit)
+		return;
+
+	dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name);
+	guard(regmap)(map);
+	map->cache_ops->exit(map);
+}
+
 int regcache_init(struct regmap *map, const struct regmap_config *config)
 {
 	bool sort_defaults = false;
@@ -222,15 +244,9 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
 		map->max_register_is_set = true;
 	}
 
-	if (map->cache_ops->init) {
-		dev_dbg(map->dev, "Initializing %s cache\n",
-			map->cache_ops->name);
-		map->lock(map->lock_arg);
-		ret = map->cache_ops->init(map);
-		map->unlock(map->lock_arg);
-		if (ret)
-			goto err_free_reg_defaults;
-	}
+	ret = regcache_locked_op(map, map->cache_ops->init, "Initializing");
+	if (ret)
+		goto err_free_reg_defaults;
 
 	/*
 	 * Some devices such as PMICs don't have cache defaults,
@@ -243,12 +259,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
 			goto err_exit;
 	}
 
-	if (map->cache_ops->populate &&
-	    (map->num_reg_defaults || map->reg_default_cb)) {
-		dev_dbg(map->dev, "Populating %s cache\n", map->cache_ops->name);
-		map->lock(map->lock_arg);
-		ret = map->cache_ops->populate(map);
-		map->unlock(map->lock_arg);
+	if (map->num_reg_defaults || map->reg_default_cb) {
+		ret = regcache_locked_op(map, map->cache_ops->populate, "Populating");
 		if (ret)
 			goto err_free;
 	}
@@ -257,12 +269,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
 err_free:
 	regcache_hw_exit(map);
 err_exit:
-	if (map->cache_ops->exit) {
-		dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name);
-		map->lock(map->lock_arg);
-		map->cache_ops->exit(map);
-		map->unlock(map->lock_arg);
-	}
+	regcache_locked_exit(map);
 err_free_reg_defaults:
 	kfree(map->reg_defaults);
 
@@ -277,13 +284,7 @@ void regcache_exit(struct regmap *map)
 	BUG_ON(!map->cache_ops);
 
 	regcache_hw_exit(map);
-
-	if (map->cache_ops->exit) {
-		dev_dbg(map->dev, "Destroying %s cache\n",
-			map->cache_ops->name);
-		scoped_guard(regmap, map)
-			map->cache_ops->exit(map);
-	}
+	regcache_locked_exit(map);
 
 	kfree(map->reg_defaults);
 }

-- 
2.51.0


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

* [PATCH 2/7] regcache: extract __regcache_sync() to deduplicate sync dispatch
  2026-09-30  9:46 [PATCH 0/7] regmap: cleanups for regcache and regmap init paths Peng Fan (OSS)
  2026-09-30  9:46 ` [PATCH 1/7] regcache: extract locked helpers to replace open-coded lock/unlock pairs Peng Fan (OSS)
@ 2026-09-30  9:46 ` Peng Fan (OSS)
  2026-09-30  9:46 ` [PATCH 3/7] regcache: simplify control flow and reduce nesting Peng Fan (OSS)
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30  9:46 UTC (permalink / raw)
  To: Mark Brown, Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: linux-kernel, driver-core, Peng Fan

From: Peng Fan <peng.fan@nxp.com>

regcache_sync() and regcache_sync_region() both open-code the same
pattern: check cache_dirty, then dispatch to either cache_ops->sync()
or regcache_default_sync().

Extract this into a static __regcache_sync() helper that both callers
share. The helper checks cache_dirty and returns 0 early when the
cache is clean, dispatches to the backend sync callback when present,
and falls back to regcache_default_sync() otherwise.

In regcache_sync_region(), the cache_dirty early-exit previously
skipped setting map->async. With the helper, map->async is set to
true before the call and restored to false unconditionally afterward,
so when cache_dirty is false the flag is toggled and immediately
restored - functionally equivalent.

No functional change.

Assisted-by: LLM
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/base/regmap/regcache.c | 28 +++++++++++++++-------------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index ebfa27bb6f54..b486cd826b3c 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -22,6 +22,8 @@ static const struct regcache_ops *cache_types[] = {
 	&regcache_flat_ops,
 };
 
+static int regcache_default_sync(struct regmap *map, unsigned int min, unsigned int max);
+
 static int regcache_defaults_cmp(const void *a, const void *b)
 {
 	const struct reg_default *x = a;
@@ -143,6 +145,17 @@ static void regcache_locked_exit(struct regmap *map)
 	map->cache_ops->exit(map);
 }
 
+static int __regcache_sync(struct regmap *map, unsigned int min, unsigned int max)
+{
+	if (!map->cache_dirty)
+		return 0;
+
+	if (map->cache_ops->sync)
+		return map->cache_ops->sync(map, min, max);
+
+	return regcache_default_sync(map, min, max);
+}
+
 int regcache_init(struct regmap *map, const struct regmap_config *config)
 {
 	bool sort_defaults = false;
@@ -461,11 +474,7 @@ int regcache_sync(struct regmap *map)
 	}
 	map->cache_bypass = false;
 
-	if (map->cache_ops->sync)
-		sync_ret = map->cache_ops->sync(map, 0, map->max_register);
-	else
-		sync_ret = regcache_default_sync(map, 0, map->max_register);
-
+	sync_ret = __regcache_sync(map, 0, map->max_register);
 	if (sync_ret == 0)
 		map->cache_dirty = false;
 
@@ -545,17 +554,10 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
 
 	trace_regcache_sync(map, name, "start region");
 
-	if (!map->cache_dirty)
-		goto out;
-
 	map->async = true;
 
-	if (map->cache_ops->sync)
-		ret = map->cache_ops->sync(map, min, max);
-	else
-		ret = regcache_default_sync(map, min, max);
+	ret = __regcache_sync(map, min, max);
 
-out:
 	/* Restore the bypass state */
 	map->cache_bypass = bypass;
 	map->async = false;

-- 
2.51.0


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

* [PATCH 3/7] regcache: simplify control flow and reduce nesting
  2026-09-30  9:46 [PATCH 0/7] regmap: cleanups for regcache and regmap init paths Peng Fan (OSS)
  2026-09-30  9:46 ` [PATCH 1/7] regcache: extract locked helpers to replace open-coded lock/unlock pairs Peng Fan (OSS)
  2026-09-30  9:46 ` [PATCH 2/7] regcache: extract __regcache_sync() to deduplicate sync dispatch Peng Fan (OSS)
@ 2026-09-30  9:46 ` Peng Fan (OSS)
  2026-09-30  9:46 ` [PATCH 4/7] regmap: use krealloc_array() in regmap_register_patch() Peng Fan (OSS)
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30  9:46 UTC (permalink / raw)
  To: Mark Brown, Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: linux-kernel, driver-core, Peng Fan

From: Peng Fan <peng.fan@nxp.com>

Move the count guard into regcache_hw_init() so callers don't need to
check before calling, and flatten conditional blocks that wrap a single
call by using early returns instead.

Merge the two separate reg_defaults / num_reg_defaults sanity checks
into a single expression, and drop unnecessary else-after-return in
regcache_lookup_reg() and regcache_sync_block().

No functional change.

Assisted-by: LLM
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/base/regmap/regcache.c | 50 +++++++++++++++++++-----------------------
 1 file changed, 22 insertions(+), 28 deletions(-)

diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index b486cd826b3c..136d6adf3120 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -58,12 +58,15 @@ static int regcache_count_cacheable_registers(struct regmap *map)
 	return count;
 }
 
-static int regcache_hw_init(struct regmap *map)
+static int regcache_hw_init(struct regmap *map, int count)
 {
 	int ret;
 	unsigned int reg, val;
 	void *tmp_buf;
 
+	if (!count)
+		return 0;
+
 	if (!map->reg_defaults_raw) {
 		bool cache_bypass = map->cache_bypass;
 		dev_dbg(map->dev, "No cache defaults, reading back from HW\n");
@@ -174,15 +177,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
 		return 0;
 	}
 
-	if (config->reg_defaults && !config->num_reg_defaults) {
-		dev_err(map->dev,
-			 "Register defaults are set without the number!\n");
-		return -EINVAL;
-	}
-
-	if (config->num_reg_defaults && !config->reg_defaults) {
-		dev_err(map->dev,
-			"Register defaults number are set without the reg!\n");
+	if (!!config->reg_defaults != !!config->num_reg_defaults) {
+		dev_err(map->dev, "reg_defaults and num_reg_defaults must both be specified\n");
 		return -EINVAL;
 	}
 
@@ -215,9 +211,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
 	map->cache = NULL;
 	map->cache_ops = cache_types[i];
 
-	if (!map->cache_ops->read ||
-	    !map->cache_ops->write ||
-	    !map->cache_ops->name)
+	if (!map->cache_ops->read || !map->cache_ops->write || !map->cache_ops->name)
 		return -EINVAL;
 
 	/* We still need to ensure that the reg_defaults
@@ -266,17 +260,17 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
 	 * we cope with this by reading back the HW registers and
 	 * crafting the cache defaults by hand.
 	 */
-	if (count) {
-		ret = regcache_hw_init(map);
-		if (ret)
-			goto err_exit;
-	}
+	ret = regcache_hw_init(map, count);
+	if (ret)
+		goto err_exit;
+
+	if (!map->num_reg_defaults && !map->reg_default_cb)
+		return 0;
+
+	ret = regcache_locked_op(map, map->cache_ops->populate, "Populating");
+	if (ret)
+		goto err_free;
 
-	if (map->num_reg_defaults || map->reg_default_cb) {
-		ret = regcache_locked_op(map, map->cache_ops->populate, "Populating");
-		if (ret)
-			goto err_free;
-	}
 	return 0;
 
 err_free:
@@ -762,8 +756,8 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg)
 
 	if (r)
 		return r - map->reg_defaults;
-	else
-		return -ENOENT;
+
+	return -ENOENT;
 }
 
 static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx)
@@ -900,7 +894,7 @@ int regcache_sync_block(struct regmap *map, void *block,
 	if (regmap_can_raw_write(map) && !map->use_single_write)
 		return regcache_sync_block_raw(map, block, cache_present,
 					       block_base, start, end);
-	else
-		return regcache_sync_block_single(map, block, cache_present,
-						  block_base, start, end);
+
+	return regcache_sync_block_single(map, block, cache_present,
+					  block_base, start, end);
 }

-- 
2.51.0


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

* [PATCH 4/7] regmap: use krealloc_array() in regmap_register_patch()
  2026-09-30  9:46 [PATCH 0/7] regmap: cleanups for regcache and regmap init paths Peng Fan (OSS)
                   ` (2 preceding siblings ...)
  2026-09-30  9:46 ` [PATCH 3/7] regcache: simplify control flow and reduce nesting Peng Fan (OSS)
@ 2026-09-30  9:46 ` Peng Fan (OSS)
  2026-09-30  9:46 ` [PATCH 5/7] regmap: use devm_kmemdup() for bus copies and remove free_on_exit Peng Fan (OSS)
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30  9:46 UTC (permalink / raw)
  To: Mark Brown, Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: linux-kernel, driver-core, Peng Fan

From: Peng Fan <peng.fan@nxp.com>

Replace krealloc() with krealloc_array() in regmap_register_patch() to
gain the built-in overflow check on the element-count * element-size
multiplication, and switch to the early-return error style to reduce
nesting.

Assisted-by: LLM
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/base/regmap/regmap.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 0b40e1e7817c..b9830afa6d8e 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -3379,16 +3379,13 @@ int regmap_register_patch(struct regmap *map, const struct reg_sequence *regs,
 	    num_regs))
 		return 0;
 
-	p = krealloc(map->patch,
-		     sizeof(struct reg_sequence) * (map->patch_regs + num_regs),
-		     GFP_KERNEL);
-	if (p) {
-		memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs));
-		map->patch = p;
-		map->patch_regs += num_regs;
-	} else {
+	p = krealloc_array(map->patch, map->patch_regs + num_regs, sizeof(*p), GFP_KERNEL);
+	if (!p)
 		return -ENOMEM;
-	}
+
+	memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs));
+	map->patch = p;
+	map->patch_regs += num_regs;
 
 	scoped_guard(regmap, map) {
 		bypass = map->cache_bypass;

-- 
2.51.0


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

* [PATCH 5/7] regmap: use devm_kmemdup() for bus copies and remove free_on_exit
  2026-09-30  9:46 [PATCH 0/7] regmap: cleanups for regcache and regmap init paths Peng Fan (OSS)
                   ` (3 preceding siblings ...)
  2026-09-30  9:46 ` [PATCH 4/7] regmap: use krealloc_array() in regmap_register_patch() Peng Fan (OSS)
@ 2026-09-30  9:46 ` Peng Fan (OSS)
  2026-09-30  9:47 ` [PATCH 6/7] regmap: return errors directly before map allocation in __regmap_init() Peng Fan (OSS)
  2026-09-30  9:47 ` [PATCH 7/7] regmap: fix potential double-free of map->reg_defaults on cache reinit Peng Fan (OSS)
  6 siblings, 0 replies; 8+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30  9:46 UTC (permalink / raw)
  To: Mark Brown, Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: linux-kernel, driver-core, Peng Fan

From: Peng Fan <peng.fan@nxp.com>

regmap_get_i2c_bus() and regmap_get_spi_bus() duplicate the static bus
struct when the transfer size needs clamping. They use kmemdup()
and set the free_on_exit flag so regmap_exit() will kfree the copy.

Replace kmemdup() with devm_kmemdup() tied to the parent device, which
lets devres handle the lifetime automatically.  This is safe because
devres cleanup is LIFO: the bus copy (allocated first) is freed after
the regmap devres action calls regmap_exit(), so the bus is guaranteed
alive while the regmap references it. For the non-devm regmap_init
path the driver calls regmap_exit() in its remove() callback, and
devres runs after remove() returns, so the ordering holds there as
well.

With no remaining producers of free_on_exit, remove the flag from
struct regmap_bus and drop the corresponding kfree() calls in
__regmap_init() and regmap_exit().

Assisted-by: LLM
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/base/regmap/regmap-i2c.c | 3 +--
 drivers/base/regmap/regmap-spi.c | 3 +--
 drivers/base/regmap/regmap.c     | 4 ----
 include/linux/regmap.h           | 2 --
 4 files changed, 2 insertions(+), 10 deletions(-)

diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index 51a04961faf7..813e8b407a0f 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -403,10 +403,9 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
 				(config->reg_bits + config->pad_bits) / BITS_PER_BYTE;
 
 		if (max_read || max_write) {
-			ret_bus = kmemdup(bus, sizeof(*bus), GFP_KERNEL);
+			ret_bus = devm_kmemdup(&i2c->dev, bus, sizeof(*bus), GFP_KERNEL);
 			if (!ret_bus)
 				return ERR_PTR(-ENOMEM);
-			ret_bus->free_on_exit = true;
 			ret_bus->max_raw_read = max_read;
 			ret_bus->max_raw_write = max_write;
 			bus = ret_bus;
diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c
index b9fec387997e..8b5a3fbcb18d 100644
--- a/drivers/base/regmap/regmap-spi.c
+++ b/drivers/base/regmap/regmap-spi.c
@@ -117,7 +117,7 @@ static const struct regmap_bus *regmap_get_spi_bus(struct spi_device *spi,
 	struct regmap_bus *bus;
 
 	if (max_size != SIZE_MAX) {
-		bus = kmemdup(&regmap_spi, sizeof(*bus), GFP_KERNEL);
+		bus = devm_kmemdup(&spi->dev, &regmap_spi, sizeof(*bus), GFP_KERNEL);
 		if (!bus)
 			return ERR_PTR(-ENOMEM);
 
@@ -126,7 +126,6 @@ static const struct regmap_bus *regmap_get_spi_bus(struct spi_device *spi,
 		if (max_size + reg_reserve_size > max_msg_size)
 			max_size -= reg_reserve_size;
 
-		bus->free_on_exit = true;
 		bus->max_raw_read = max_size;
 		bus->max_raw_write = max_size;
 
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index b9830afa6d8e..bb5553ce58d1 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1175,8 +1175,6 @@ struct regmap *__regmap_init(struct device *dev,
 err_map:
 	kfree(map);
 err:
-	if (bus && bus->free_on_exit)
-		kfree(bus);
 	return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(__regmap_init);
@@ -1479,8 +1477,6 @@ void regmap_exit(struct regmap *map)
 		mutex_destroy(&map->mutex);
 	kfree_const(map->name);
 	kfree(map->patch);
-	if (map->bus && map->bus->free_on_exit)
-		kfree(map->bus);
 	kfree(map);
 }
 EXPORT_SYMBOL_GPL(regmap_exit);
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index c8aebd148e08..f8334a37bd0e 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -579,7 +579,6 @@ typedef void (*regmap_hw_free_context)(void *context);
  *	     to perform locking. This field is ignored if custom lock/unlock
  *	     functions are used (see fields lock/unlock of
  *	     struct regmap_config).
- * @free_on_exit: kfree this on exit of regmap
  * @write: Write operation.
  * @gather_write: Write operation with split register/value, return -ENOTSUPP
  *                if not implemented  on a given device.
@@ -613,7 +612,6 @@ typedef void (*regmap_hw_free_context)(void *context);
  */
 struct regmap_bus {
 	bool fast_io;
-	bool free_on_exit;
 	regmap_hw_write write;
 	regmap_hw_gather_write gather_write;
 	regmap_hw_async_write async_write;

-- 
2.51.0


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

* [PATCH 6/7] regmap: return errors directly before map allocation in __regmap_init()
  2026-09-30  9:46 [PATCH 0/7] regmap: cleanups for regcache and regmap init paths Peng Fan (OSS)
                   ` (4 preceding siblings ...)
  2026-09-30  9:46 ` [PATCH 5/7] regmap: use devm_kmemdup() for bus copies and remove free_on_exit Peng Fan (OSS)
@ 2026-09-30  9:47 ` Peng Fan (OSS)
  2026-09-30  9:47 ` [PATCH 7/7] regmap: fix potential double-free of map->reg_defaults on cache reinit Peng Fan (OSS)
  6 siblings, 0 replies; 8+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30  9:47 UTC (permalink / raw)
  To: Mark Brown, Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: linux-kernel, driver-core, Peng Fan

From: Peng Fan <peng.fan@nxp.com>

The two early error paths in __regmap_init() - a NULL config check and a
failed kzalloc - jumped to the 'err' label which only did return
ERR_PTR(ret). Since neither path has anything to clean up (the map
hasn't been allocated yet), replace the gotos with direct return
ERR_PTR() calls.

Removes the 'err' label entirely and the pre-initialisation of ret to
simplify code.

No functional change.

Assisted-by: LLM
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/base/regmap/regmap.c | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index bb5553ce58d1..88000d535985 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -681,18 +681,15 @@ struct regmap *__regmap_init(struct device *dev,
 			     const char *lock_name)
 {
 	struct regmap *map;
-	int ret = -EINVAL;
 	enum regmap_endian reg_endian, val_endian;
-	int i, j;
+	int i, j, ret;
 
 	if (!config)
-		goto err;
+		return ERR_PTR(-EINVAL);
 
 	map = kzalloc_obj(*map);
-	if (map == NULL) {
-		ret = -ENOMEM;
-		goto err;
-	}
+	if (map == NULL)
+		return ERR_PTR(-ENOMEM);
 
 	ret = regmap_set_name(map, config);
 	if (ret)
@@ -1174,7 +1171,6 @@ struct regmap *__regmap_init(struct device *dev,
 	kfree_const(map->name);
 err_map:
 	kfree(map);
-err:
 	return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(__regmap_init);

-- 
2.51.0


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

* [PATCH 7/7] regmap: fix potential double-free of map->reg_defaults on cache reinit
  2026-09-30  9:46 [PATCH 0/7] regmap: cleanups for regcache and regmap init paths Peng Fan (OSS)
                   ` (5 preceding siblings ...)
  2026-09-30  9:47 ` [PATCH 6/7] regmap: return errors directly before map allocation in __regmap_init() Peng Fan (OSS)
@ 2026-09-30  9:47 ` Peng Fan (OSS)
  6 siblings, 0 replies; 8+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30  9:47 UTC (permalink / raw)
  To: Mark Brown, Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: linux-kernel, driver-core, Peng Fan

From: Peng Fan <peng.fan@nxp.com>

regcache_exit() frees map->reg_defaults but does not NULL the pointer.
If regmap_reinit_cache() is later called with a config that has neither
reg_defaults nor num_reg_defaults_raw, regcache_init() skips both
allocation branches and leaves the stale pointer in place.  Should
cache_ops->init then fail, the err_free_reg_defaults error path calls
kfree(map->reg_defaults) a second time.

NULL the pointer after freeing so the error-path kfree() is a harmless
no-op.

Assisted-by: LLM
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/base/regmap/regcache.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 136d6adf3120..3b1c2f28d585 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -294,6 +294,7 @@ void regcache_exit(struct regmap *map)
 	regcache_locked_exit(map);
 
 	kfree(map->reg_defaults);
+	map->reg_defaults = NULL;
 }
 
 /**

-- 
2.51.0


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

end of thread, other threads:[~2026-09-30  9:50 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-30  9:46 [PATCH 0/7] regmap: cleanups for regcache and regmap init paths Peng Fan (OSS)
2026-09-30  9:46 ` [PATCH 1/7] regcache: extract locked helpers to replace open-coded lock/unlock pairs Peng Fan (OSS)
2026-09-30  9:46 ` [PATCH 2/7] regcache: extract __regcache_sync() to deduplicate sync dispatch Peng Fan (OSS)
2026-09-30  9:46 ` [PATCH 3/7] regcache: simplify control flow and reduce nesting Peng Fan (OSS)
2026-09-30  9:46 ` [PATCH 4/7] regmap: use krealloc_array() in regmap_register_patch() Peng Fan (OSS)
2026-09-30  9:46 ` [PATCH 5/7] regmap: use devm_kmemdup() for bus copies and remove free_on_exit Peng Fan (OSS)
2026-09-30  9:47 ` [PATCH 6/7] regmap: return errors directly before map allocation in __regmap_init() Peng Fan (OSS)
2026-09-30  9:47 ` [PATCH 7/7] regmap: fix potential double-free of map->reg_defaults on cache reinit Peng Fan (OSS)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®