diff options
Diffstat (limited to 'drivers/base/devres.c')
-rw-r--r-- | drivers/base/devres.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/drivers/base/devres.c b/drivers/base/devres.c index db4e264eecb..69d9b0c89a0 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -817,6 +817,61 @@ char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp) EXPORT_SYMBOL_GPL(devm_kstrdup); /** + * devm_kvasprintf - Allocate resource managed space + * for the formatted string. + * @dev: Device to allocate memory for + * @gfp: the GFP mask used in the devm_kmalloc() call when + * allocating memory + * @fmt: the formatted string to duplicate + * @ap: the list of tokens to be placed in the formatted string + * RETURNS: + * Pointer to allocated string on success, NULL on failure. + */ +char *devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt, + va_list ap) +{ + unsigned int len; + char *p; + va_list aq; + + va_copy(aq, ap); + len = vsnprintf(NULL, 0, fmt, aq); + va_end(aq); + + p = devm_kmalloc(dev, len+1, gfp); + if (!p) + return NULL; + + vsnprintf(p, len+1, fmt, ap); + + return p; +} +EXPORT_SYMBOL(devm_kvasprintf); + +/** + * devm_kasprintf - Allocate resource managed space + * and copy an existing formatted string into that + * @dev: Device to allocate memory for + * @gfp: the GFP mask used in the devm_kmalloc() call when + * allocating memory + * @fmt: the string to duplicate + * RETURNS: + * Pointer to allocated string on success, NULL on failure. + */ +char *devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...) +{ + va_list ap; + char *p; + + va_start(ap, fmt); + p = devm_kvasprintf(dev, gfp, fmt, ap); + va_end(ap); + + return p; +} +EXPORT_SYMBOL_GPL(devm_kasprintf); + +/** * devm_kfree - Resource-managed kfree * @dev: Device this memory belongs to * @p: Memory to free @@ -831,3 +886,100 @@ void devm_kfree(struct device *dev, void *p) WARN_ON(rc); } EXPORT_SYMBOL_GPL(devm_kfree); + +/** + * devm_kmemdup - Resource-managed kmemdup + * @dev: Device this memory belongs to + * @src: Memory region to duplicate + * @len: Memory region length + * @gfp: GFP mask to use + * + * Duplicate region of a memory using resource managed kmalloc + */ +void *devm_kmemdup(struct device *dev, const void *src, size_t len, gfp_t gfp) +{ + void *p; + + p = devm_kmalloc(dev, len, gfp); + if (p) + memcpy(p, src, len); + + return p; +} +EXPORT_SYMBOL_GPL(devm_kmemdup); + +struct pages_devres { + unsigned long addr; + unsigned int order; +}; + +static int devm_pages_match(struct device *dev, void *res, void *p) +{ + struct pages_devres *devres = res; + struct pages_devres *target = p; + + return devres->addr == target->addr; +} + +static void devm_pages_release(struct device *dev, void *res) +{ + struct pages_devres *devres = res; + + free_pages(devres->addr, devres->order); +} + +/** + * devm_get_free_pages - Resource-managed __get_free_pages + * @dev: Device to allocate memory for + * @gfp_mask: Allocation gfp flags + * @order: Allocation size is (1 << order) pages + * + * Managed get_free_pages. Memory allocated with this function is + * automatically freed on driver detach. + * + * RETURNS: + * Address of allocated memory on success, 0 on failure. + */ + +unsigned long devm_get_free_pages(struct device *dev, + gfp_t gfp_mask, unsigned int order) +{ + struct pages_devres *devres; + unsigned long addr; + + addr = __get_free_pages(gfp_mask, order); + + if (unlikely(!addr)) + return 0; + + devres = devres_alloc(devm_pages_release, + sizeof(struct pages_devres), GFP_KERNEL); + if (unlikely(!devres)) { + free_pages(addr, order); + return 0; + } + + devres->addr = addr; + devres->order = order; + + devres_add(dev, devres); + return addr; +} +EXPORT_SYMBOL_GPL(devm_get_free_pages); + +/** + * devm_free_pages - Resource-managed free_pages + * @dev: Device this memory belongs to + * @addr: Memory to free + * + * Free memory allocated with devm_get_free_pages(). Unlike free_pages, + * there is no need to supply the @order. + */ +void devm_free_pages(struct device *dev, unsigned long addr) +{ + struct pages_devres devres = { .addr = addr }; + + WARN_ON(devres_release(dev, devm_pages_release, devm_pages_match, + &devres)); +} +EXPORT_SYMBOL_GPL(devm_free_pages); |