[PATCH RFC 11/40] devres: add devm_krealloc
Casey Connolly <[email protected]> Thu, 19 Mar 2026 21:56:33 +0100
| Newsgroups | io.groups.u-boot-amlogic,org.u-boot-project.lists.u-boot |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- 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