[PATCH RFC 11/40] devres: add devm_krealloc
Casey Connolly
casey.connolly at linaro.org
Thu Mar 19 21:56:33 CET 2026
Loosely based on the Linux version, this makes it possible to krealloc()
devm managed memory. Currently it depends on tracking the allocation
size in struct devres even when DEBUG_DEVRES is disabled, but it's a
relatively small tradeoff for the additional functionality.
Signed-off-by: Casey Connolly <casey.connolly at linaro.org>
---
drivers/core/devres.c | 83 ++++++++++++++++++++++++++++++++++++++++++++-------
include/dm/devres.h | 10 +++++++
2 files changed, 83 insertions(+), 10 deletions(-)
diff --git a/drivers/core/devres.c b/drivers/core/devres.c
index 8df08b91021c..88f7c832f43e 100644
--- a/drivers/core/devres.c
+++ b/drivers/core/devres.c
@@ -45,18 +45,17 @@ struct devres {
dr_release_t release;
enum devres_phase phase;
#ifdef CONFIG_DEBUG_DEVRES
const char *name;
- size_t size;
#endif
+ size_t size;
unsigned long long data[];
};
#ifdef CONFIG_DEBUG_DEVRES
-static void set_node_dbginfo(struct devres *dr, const char *name, size_t size)
+static void set_node_dbginfo(struct devres *dr, const char *name)
{
dr->name = name;
- dr->size = size;
}
static void devres_log(struct udevice *dev, struct devres *dr,
const char *op)
@@ -64,9 +63,9 @@ static void devres_log(struct udevice *dev, struct devres *dr,
log_debug("%s: DEVRES %3s %p %s (%lu bytes)\n", dev->name, op, dr,
dr->name, (unsigned long)dr->size);
}
#else /* CONFIG_DEBUG_DEVRES */
-#define set_node_dbginfo(dr, n, s) do {} while (0)
+#define set_node_dbginfo(dr, n) do {} while (0)
#define devres_log(dev, dr, op) do {} while (0)
#endif
#if CONFIG_DEBUG_DEVRES
@@ -84,9 +83,10 @@ void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
return NULL;
INIT_LIST_HEAD(&dr->entry);
dr->release = release;
- set_node_dbginfo(dr, name, size);
+ dr->size = size;
+ set_node_dbginfo(dr, name);
return dr->data;
}
@@ -99,23 +99,35 @@ void devres_free(void *res)
kfree(dr);
}
}
-void devres_add(struct udevice *dev, void *res)
+static void devres_set_phase(struct udevice *dev, struct devres *dr)
{
- struct devres *dr = container_of(res, struct devres, data);
-
- devres_log(dev, dr, "ADD");
- assert_noisy(list_empty(&dr->entry));
if (dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID)
dr->phase = DEVRES_PHASE_PROBE;
else if (dev_get_flags(dev) & DM_FLAG_BOUND)
dr->phase = DEVRES_PHASE_OFDATA;
else
dr->phase = DEVRES_PHASE_BIND;
+}
+
+void devres_add(struct udevice *dev, void *res)
+{
+ struct devres *dr = container_of(res, struct devres, data);
+
+ devres_log(dev, dr, "ADD");
+ assert_noisy(list_empty(&dr->entry));
+ devres_set_phase(dev, dr);
list_add_tail(&dr->entry, &dev->devres_head);
}
+static void devres_replace(struct udevice *dev, struct devres *old, struct devres *new)
+{
+ devres_log(dev, old, "REPLACE");
+ devres_set_phase(dev, new);
+ list_replace(&old->entry, &new->entry);
+}
+
void *devres_find(struct udevice *dev, dr_release_t release,
dr_match_t match, void *match_data)
{
struct devres *dr;
@@ -290,4 +302,55 @@ void devm_kfree(struct udevice *dev, void *p)
rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p);
assert_noisy(!rc);
}
+
+/**
+ * devm_krealloc - Resource-managed krealloc()
+ * @dev: Device to re-allocate memory for
+ * @ptr: Pointer to the memory chunk to re-allocate
+ * @new_size: New allocation size
+ * @gfp: Allocation gfp flags
+ *
+ * Managed krealloc(). Resizes the memory chunk allocated with devm_kmalloc().
+ * Behaves similarly to regular krealloc(): if @ptr is NULL or ZERO_SIZE_PTR,
+ * it's the equivalent of devm_kmalloc(). If new_size is zero, it frees the
+ * previously allocated memory and returns ZERO_SIZE_PTR. This function doesn't
+ * change the order in which the release callback for the re-alloc'ed devres
+ * will be called (except when falling back to devm_kmalloc() or when freeing
+ * resources when new_size is zero). The contents of the memory are preserved
+ * up to the lesser of new and old sizes.
+ */
+void *devm_krealloc(struct udevice *dev, void *ptr, size_t new_size, gfp_t gfp)
+{
+ struct devres *old_dr, *new_dr;
+
+ if (unlikely(!new_size)) {
+ devm_kfree(dev, ptr);
+ return NULL;
+ }
+
+ if (unlikely(!ptr))
+ return devm_kmalloc(dev, new_size, gfp);
+
+ old_dr = devres_find(dev, devm_kmalloc_release, devm_kmalloc_match, ptr);
+ if (!old_dr) {
+ printf("Devres realloc failed, memory chunk not managed or managed by a different device\n");
+ return NULL;
+ }
+
+ /* No need to realloc to shrink */
+ if (old_dr->size < new_size)
+ return old_dr->data;
+
+ new_dr = _devres_alloc(devm_kmalloc_release, new_size, gfp);
+ if (!new_dr)
+ return NULL;
+
+ devres_replace(dev, old_dr, new_dr);
+ memcpy(new_dr->data, old_dr->data,
+ old_dr->size);
+
+ devres_free(old_dr->data);
+
+ return new_dr->data;
+}
diff --git a/include/dm/devres.h b/include/dm/devres.h
index 406d18686f4d..13d7d0eec43a 100644
--- a/include/dm/devres.h
+++ b/include/dm/devres.h
@@ -194,8 +194,18 @@ static inline void *devm_kcalloc(struct udevice *dev,
{
return devm_kmalloc_array(dev, n, size, flags | __GFP_ZERO);
}
+void *devm_krealloc(struct udevice *dev, void *ptr, size_t new_size, gfp_t gfp);
+
+static inline void *devm_krealloc_array(struct udevice *dev, void *p, size_t new_n, size_t new_size, gfp_t flags)
+{
+ if (new_size != 0 && new_n > SIZE_MAX / new_size)
+ return NULL;
+
+ return devm_krealloc(dev, p, new_n * new_size, flags);
+}
+
/**
* devm_kfree() - Resource-managed kfree
* @dev: Device this memory belongs to
* @ptr: Memory to free
--
2.51.0
More information about the U-Boot
mailing list