forked from Minki/linux
mm/util: add kstrdup_const
kstrdup() is often used to duplicate strings where neither source neither destination will be ever modified. In such case we can just reuse the source instead of duplicating it. The problem is that we must be sure that the source is non-modifiable and its life-time is long enough. I suspect the good candidates for such strings are strings located in kernel .rodata section, they cannot be modifed because the section is read-only and their life-time is equal to kernel life-time. This small patchset proposes alternative version of kstrdup - kstrdup_const, which returns source string if it is located in .rodata otherwise it fallbacks to kstrdup. To verify if the source is in .rodata function checks if the address is between sentinels __start_rodata, __end_rodata. I guess it should work with all architectures. The main patch is accompanied by four patches constifying kstrdup for cases where situtation described above happens frequently. I have tested the patchset on mobile platform (exynos4210-trats) and it saves 3272 string allocations. Since minimal allocation is 32 or 64 bytes depending on Kconfig options the patchset saves respectively about 100KB or 200KB of memory. Stats from tested platform show that the main offender is sysfs: By caller: 2260 __kernfs_new_node 631 clk_register+0xc8/0x1b8 318 clk_register+0x34/0x1b8 51 kmem_cache_create 12 alloc_vfsmnt By string (with count >= 5): 883 power 876 subsystem 135 parameters 132 device 61 iommu_group ... This patch (of 5): Add an alternative version of kstrdup which returns pointer to constant char array. The function checks if input string is in persistent and read-only memory section, if yes it returns the input string, otherwise it fallbacks to kstrdup. kstrdup_const is accompanied by kfree_const performing conditional memory deallocation of the string. Signed-off-by: Andrzej Hajda <a.hajda@samsung.com> Cc: Marek Szyprowski <m.szyprowski@samsung.com> Cc: Kyungmin Park <kyungmin.park@samsung.com> Cc: Mike Turquette <mturquette@linaro.org> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Christoph Lameter <cl@linux.com> Cc: Pekka Enberg <penberg@kernel.org> Cc: David Rientjes <rientjes@google.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: Tejun Heo <tj@kernel.org> Cc: Greg KH <greg@kroah.com> Cc: Geert Uytterhoeven <geert@linux-m68k.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
f5e38b9284
commit
a4bb1e43e2
@ -112,7 +112,10 @@ extern void * memchr(const void *,int,__kernel_size_t);
|
|||||||
#endif
|
#endif
|
||||||
void *memchr_inv(const void *s, int c, size_t n);
|
void *memchr_inv(const void *s, int c, size_t n);
|
||||||
|
|
||||||
|
extern void kfree_const(const void *x);
|
||||||
|
|
||||||
extern char *kstrdup(const char *s, gfp_t gfp);
|
extern char *kstrdup(const char *s, gfp_t gfp);
|
||||||
|
extern const char *kstrdup_const(const char *s, gfp_t gfp);
|
||||||
extern char *kstrndup(const char *s, size_t len, gfp_t gfp);
|
extern char *kstrndup(const char *s, size_t len, gfp_t gfp);
|
||||||
extern void *kmemdup(const void *src, size_t len, gfp_t gfp);
|
extern void *kmemdup(const void *src, size_t len, gfp_t gfp);
|
||||||
|
|
||||||
|
38
mm/util.c
38
mm/util.c
@ -12,10 +12,30 @@
|
|||||||
#include <linux/hugetlb.h>
|
#include <linux/hugetlb.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
|
|
||||||
|
#include <asm/sections.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
|
static inline int is_kernel_rodata(unsigned long addr)
|
||||||
|
{
|
||||||
|
return addr >= (unsigned long)__start_rodata &&
|
||||||
|
addr < (unsigned long)__end_rodata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfree_const - conditionally free memory
|
||||||
|
* @x: pointer to the memory
|
||||||
|
*
|
||||||
|
* Function calls kfree only if @x is not in .rodata section.
|
||||||
|
*/
|
||||||
|
void kfree_const(const void *x)
|
||||||
|
{
|
||||||
|
if (!is_kernel_rodata((unsigned long)x))
|
||||||
|
kfree(x);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(kfree_const);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kstrdup - allocate space for and copy an existing string
|
* kstrdup - allocate space for and copy an existing string
|
||||||
* @s: the string to duplicate
|
* @s: the string to duplicate
|
||||||
@ -37,6 +57,24 @@ char *kstrdup(const char *s, gfp_t gfp)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(kstrdup);
|
EXPORT_SYMBOL(kstrdup);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kstrdup_const - conditionally duplicate an existing const string
|
||||||
|
* @s: the string to duplicate
|
||||||
|
* @gfp: the GFP mask used in the kmalloc() call when allocating memory
|
||||||
|
*
|
||||||
|
* Function returns source string if it is in .rodata section otherwise it
|
||||||
|
* fallbacks to kstrdup.
|
||||||
|
* Strings allocated by kstrdup_const should be freed by kfree_const.
|
||||||
|
*/
|
||||||
|
const char *kstrdup_const(const char *s, gfp_t gfp)
|
||||||
|
{
|
||||||
|
if (is_kernel_rodata((unsigned long)s))
|
||||||
|
return s;
|
||||||
|
|
||||||
|
return kstrdup(s, gfp);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(kstrdup_const);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kstrndup - allocate space for and copy an existing string
|
* kstrndup - allocate space for and copy an existing string
|
||||||
* @s: the string to duplicate
|
* @s: the string to duplicate
|
||||||
|
Loading…
Reference in New Issue
Block a user