lib/kobject.c: use kvasprintf_const for formatting ->name
Sometimes kobject_set_name_vargs is called with a format string conaining no %, or a format string of precisely "%s", where the single vararg happens to point to .rodata. kvasprintf_const detects these cases for us and returns a copy of that pointer instead of duplicating the string, thus saving some run-time memory. Otherwise, it falls back to kvasprintf. We just need to always deallocate ->name using kfree_const. Unfortunately, the dance we need to do to perform the '/' -> '!' sanitization makes the resulting code rather ugly. I instrumented kstrdup_const to provide some statistics on the memory saved, and for me this gave an additional ~14KB after boot (306KB was already saved; this patch bumped that to 320KB). I have KMALLOC_SHIFT_LOW==3, and since 80% of the kvasprintf_const hits were satisfied by an 8-byte allocation, the 14K would roughly be quadrupled when KMALLOC_SHIFT_LOW==5. Whether these numbers are sufficient to justify the ugliness I'll leave to others to decide. Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk> Cc: Greg KH <greg@kroah.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
0a9df786a6
commit
f773f32d71
@ -257,18 +257,32 @@ static int kobject_add_internal(struct kobject *kobj)
|
|||||||
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
|
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
|
||||||
va_list vargs)
|
va_list vargs)
|
||||||
{
|
{
|
||||||
char *s;
|
const char *s;
|
||||||
|
|
||||||
if (kobj->name && !fmt)
|
if (kobj->name && !fmt)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
s = kvasprintf(GFP_KERNEL, fmt, vargs);
|
s = kvasprintf_const(GFP_KERNEL, fmt, vargs);
|
||||||
if (!s)
|
if (!s)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* ewww... some of these buggers have '/' in the name ... */
|
/*
|
||||||
strreplace(s, '/', '!');
|
* ewww... some of these buggers have '/' in the name ... If
|
||||||
kfree(kobj->name);
|
* that's the case, we need to make sure we have an actual
|
||||||
|
* allocated copy to modify, since kvasprintf_const may have
|
||||||
|
* returned something from .rodata.
|
||||||
|
*/
|
||||||
|
if (strchr(s, '/')) {
|
||||||
|
char *t;
|
||||||
|
|
||||||
|
t = kstrdup(s, GFP_KERNEL);
|
||||||
|
kfree_const(s);
|
||||||
|
if (!t)
|
||||||
|
return -ENOMEM;
|
||||||
|
strreplace(t, '/', '!');
|
||||||
|
s = t;
|
||||||
|
}
|
||||||
|
kfree_const(kobj->name);
|
||||||
kobj->name = s;
|
kobj->name = s;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -466,7 +480,7 @@ int kobject_rename(struct kobject *kobj, const char *new_name)
|
|||||||
envp[0] = devpath_string;
|
envp[0] = devpath_string;
|
||||||
envp[1] = NULL;
|
envp[1] = NULL;
|
||||||
|
|
||||||
name = dup_name = kstrdup(new_name, GFP_KERNEL);
|
name = dup_name = kstrdup_const(new_name, GFP_KERNEL);
|
||||||
if (!name) {
|
if (!name) {
|
||||||
error = -ENOMEM;
|
error = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
@ -486,7 +500,7 @@ int kobject_rename(struct kobject *kobj, const char *new_name)
|
|||||||
kobject_uevent_env(kobj, KOBJ_MOVE, envp);
|
kobject_uevent_env(kobj, KOBJ_MOVE, envp);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
kfree(dup_name);
|
kfree_const(dup_name);
|
||||||
kfree(devpath_string);
|
kfree(devpath_string);
|
||||||
kfree(devpath);
|
kfree(devpath);
|
||||||
kobject_put(kobj);
|
kobject_put(kobj);
|
||||||
@ -634,7 +648,7 @@ static void kobject_cleanup(struct kobject *kobj)
|
|||||||
/* free name if we allocated it */
|
/* free name if we allocated it */
|
||||||
if (name) {
|
if (name) {
|
||||||
pr_debug("kobject: '%s': free name\n", name);
|
pr_debug("kobject: '%s': free name\n", name);
|
||||||
kfree(name);
|
kfree_const(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user