mm, slub: extend slub_debug syntax for multiple blocks
Patch series "slub_debug fixes and improvements". The slub_debug kernel boot parameter can either apply a single set of options to all caches or a list of caches. There is a use case where debugging is applied for all caches and then disabled at runtime for specific caches, for performance and memory consumption reasons [1]. As runtime changes are dangerous, extend the boot parameter syntax so that multiple blocks of either global or slab-specific options can be specified, with blocks delimited by ';'. This will also support the use case of [1] without runtime changes. For details see the updated Documentation/vm/slub.rst [1] https://lore.kernel.org/r/1383cd32-1ddc-4dac-b5f8-9c42282fa81c@codeaurora.org [weiyongjun1@huawei.com: make parse_slub_debug_flags() static] Link: http://lkml.kernel.org/r/20200702150522.4940-1-weiyongjun1@huawei.com Signed-off-by: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Kees Cook <keescook@chromium.org> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Christoph Lameter <cl@linux.com> Cc: Jann Horn <jannh@google.com> Cc: Roman Gushchin <guro@fb.com> Cc: Vijayanand Jitta <vjitta@codeaurora.org> Cc: Pekka Enberg <penberg@kernel.org> Cc: David Rientjes <rientjes@google.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Link: http://lkml.kernel.org/r/20200610163135.17364-2-vbabka@suse.cz Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Родитель
221503e128
Коммит
e17f1dfba3
|
@ -4689,7 +4689,7 @@
|
||||||
fragmentation. Defaults to 1 for systems with
|
fragmentation. Defaults to 1 for systems with
|
||||||
more than 32MB of RAM, 0 otherwise.
|
more than 32MB of RAM, 0 otherwise.
|
||||||
|
|
||||||
slub_debug[=options[,slabs]] [MM, SLUB]
|
slub_debug[=options[,slabs][;[options[,slabs]]...] [MM, SLUB]
|
||||||
Enabling slub_debug allows one to determine the
|
Enabling slub_debug allows one to determine the
|
||||||
culprit if slab objects become corrupted. Enabling
|
culprit if slab objects become corrupted. Enabling
|
||||||
slub_debug can create guard zones around objects and
|
slub_debug can create guard zones around objects and
|
||||||
|
|
|
@ -41,6 +41,11 @@ slub_debug=<Debug-Options>,<slab name1>,<slab name2>,...
|
||||||
Enable options only for select slabs (no spaces
|
Enable options only for select slabs (no spaces
|
||||||
after a comma)
|
after a comma)
|
||||||
|
|
||||||
|
Multiple blocks of options for all slabs or selected slabs can be given, with
|
||||||
|
blocks of options delimited by ';'. The last of "all slabs" blocks is applied
|
||||||
|
to all slabs except those that match one of the "select slabs" block. Options
|
||||||
|
of the first "select slabs" blocks that matches the slab's name are applied.
|
||||||
|
|
||||||
Possible debug options are::
|
Possible debug options are::
|
||||||
|
|
||||||
F Sanity checks on (enables SLAB_DEBUG_CONSISTENCY_CHECKS
|
F Sanity checks on (enables SLAB_DEBUG_CONSISTENCY_CHECKS
|
||||||
|
@ -83,6 +88,19 @@ switch off debugging for such caches by default, use::
|
||||||
|
|
||||||
slub_debug=O
|
slub_debug=O
|
||||||
|
|
||||||
|
You can apply different options to different list of slab names, using blocks
|
||||||
|
of options. This will enable red zoning for dentry and user tracking for
|
||||||
|
kmalloc. All other slabs will not get any debugging enabled::
|
||||||
|
|
||||||
|
slub_debug=Z,dentry;U,kmalloc-*
|
||||||
|
|
||||||
|
You can also enable options (e.g. sanity checks and poisoning) for all caches
|
||||||
|
except some that are deemed too performance critical and don't need to be
|
||||||
|
debugged by specifying global debug options followed by a list of slab names
|
||||||
|
with "-" as options::
|
||||||
|
|
||||||
|
slub_debug=FZ;-,zs_handle,zspage
|
||||||
|
|
||||||
In case you forgot to enable debugging on the kernel command line: It is
|
In case you forgot to enable debugging on the kernel command line: It is
|
||||||
possible to enable debugging manually when the kernel is up. Look at the
|
possible to enable debugging manually when the kernel is up. Look at the
|
||||||
contents of::
|
contents of::
|
||||||
|
|
213
mm/slub.c
213
mm/slub.c
|
@ -499,7 +499,7 @@ static slab_flags_t slub_debug = DEBUG_DEFAULT_FLAGS;
|
||||||
static slab_flags_t slub_debug;
|
static slab_flags_t slub_debug;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static char *slub_debug_slabs;
|
static char *slub_debug_string;
|
||||||
static int disable_higher_order_debug;
|
static int disable_higher_order_debug;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1262,8 +1262,102 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a block of slub_debug options. Blocks are delimited by ';'
|
||||||
|
*
|
||||||
|
* @str: start of block
|
||||||
|
* @flags: returns parsed flags, or DEBUG_DEFAULT_FLAGS if none specified
|
||||||
|
* @slabs: return start of list of slabs, or NULL when there's no list
|
||||||
|
* @init: assume this is initial parsing and not per-kmem-create parsing
|
||||||
|
*
|
||||||
|
* returns the start of next block if there's any, or NULL
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
parse_slub_debug_flags(char *str, slab_flags_t *flags, char **slabs, bool init)
|
||||||
|
{
|
||||||
|
bool higher_order_disable = false;
|
||||||
|
|
||||||
|
/* Skip any completely empty blocks */
|
||||||
|
while (*str && *str == ';')
|
||||||
|
str++;
|
||||||
|
|
||||||
|
if (*str == ',') {
|
||||||
|
/*
|
||||||
|
* No options but restriction on slabs. This means full
|
||||||
|
* debugging for slabs matching a pattern.
|
||||||
|
*/
|
||||||
|
*flags = DEBUG_DEFAULT_FLAGS;
|
||||||
|
goto check_slabs;
|
||||||
|
}
|
||||||
|
*flags = 0;
|
||||||
|
|
||||||
|
/* Determine which debug features should be switched on */
|
||||||
|
for (; *str && *str != ',' && *str != ';'; str++) {
|
||||||
|
switch (tolower(*str)) {
|
||||||
|
case '-':
|
||||||
|
*flags = 0;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
*flags |= SLAB_CONSISTENCY_CHECKS;
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
*flags |= SLAB_RED_ZONE;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
*flags |= SLAB_POISON;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
*flags |= SLAB_STORE_USER;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
*flags |= SLAB_TRACE;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
*flags |= SLAB_FAILSLAB;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
/*
|
||||||
|
* Avoid enabling debugging on caches if its minimum
|
||||||
|
* order would increase as a result.
|
||||||
|
*/
|
||||||
|
higher_order_disable = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (init)
|
||||||
|
pr_err("slub_debug option '%c' unknown. skipped\n", *str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check_slabs:
|
||||||
|
if (*str == ',')
|
||||||
|
*slabs = ++str;
|
||||||
|
else
|
||||||
|
*slabs = NULL;
|
||||||
|
|
||||||
|
/* Skip over the slab list */
|
||||||
|
while (*str && *str != ';')
|
||||||
|
str++;
|
||||||
|
|
||||||
|
/* Skip any completely empty blocks */
|
||||||
|
while (*str && *str == ';')
|
||||||
|
str++;
|
||||||
|
|
||||||
|
if (init && higher_order_disable)
|
||||||
|
disable_higher_order_debug = 1;
|
||||||
|
|
||||||
|
if (*str)
|
||||||
|
return str;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int __init setup_slub_debug(char *str)
|
static int __init setup_slub_debug(char *str)
|
||||||
{
|
{
|
||||||
|
slab_flags_t flags;
|
||||||
|
char *saved_str;
|
||||||
|
char *slab_list;
|
||||||
|
bool global_slub_debug_changed = false;
|
||||||
|
bool slab_list_specified = false;
|
||||||
|
|
||||||
slub_debug = DEBUG_DEFAULT_FLAGS;
|
slub_debug = DEBUG_DEFAULT_FLAGS;
|
||||||
if (*str++ != '=' || !*str)
|
if (*str++ != '=' || !*str)
|
||||||
/*
|
/*
|
||||||
|
@ -1271,59 +1365,29 @@ static int __init setup_slub_debug(char *str)
|
||||||
*/
|
*/
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (*str == ',')
|
saved_str = str;
|
||||||
/*
|
while (str) {
|
||||||
* No options but restriction on slabs. This means full
|
str = parse_slub_debug_flags(str, &flags, &slab_list, true);
|
||||||
* debugging for slabs matching a pattern.
|
|
||||||
*/
|
|
||||||
goto check_slabs;
|
|
||||||
|
|
||||||
slub_debug = 0;
|
if (!slab_list) {
|
||||||
if (*str == '-')
|
slub_debug = flags;
|
||||||
/*
|
global_slub_debug_changed = true;
|
||||||
* Switch off all debugging measures.
|
} else {
|
||||||
*/
|
slab_list_specified = true;
|
||||||
goto out;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determine which debug features should be switched on
|
|
||||||
*/
|
|
||||||
for (; *str && *str != ','; str++) {
|
|
||||||
switch (tolower(*str)) {
|
|
||||||
case 'f':
|
|
||||||
slub_debug |= SLAB_CONSISTENCY_CHECKS;
|
|
||||||
break;
|
|
||||||
case 'z':
|
|
||||||
slub_debug |= SLAB_RED_ZONE;
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
slub_debug |= SLAB_POISON;
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
slub_debug |= SLAB_STORE_USER;
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
slub_debug |= SLAB_TRACE;
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
slub_debug |= SLAB_FAILSLAB;
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
/*
|
|
||||||
* Avoid enabling debugging on caches if its minimum
|
|
||||||
* order would increase as a result.
|
|
||||||
*/
|
|
||||||
disable_higher_order_debug = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pr_err("slub_debug option '%c' unknown. skipped\n",
|
|
||||||
*str);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
check_slabs:
|
/*
|
||||||
if (*str == ',')
|
* For backwards compatibility, a single list of flags with list of
|
||||||
slub_debug_slabs = str + 1;
|
* slabs means debugging is only enabled for those slabs, so the global
|
||||||
|
* slub_debug should be 0. We can extended that to multiple lists as
|
||||||
|
* long as there is no option specifying flags without a slab list.
|
||||||
|
*/
|
||||||
|
if (slab_list_specified) {
|
||||||
|
if (!global_slub_debug_changed)
|
||||||
|
slub_debug = 0;
|
||||||
|
slub_debug_string = saved_str;
|
||||||
|
}
|
||||||
out:
|
out:
|
||||||
if ((static_branch_unlikely(&init_on_alloc) ||
|
if ((static_branch_unlikely(&init_on_alloc) ||
|
||||||
static_branch_unlikely(&init_on_free)) &&
|
static_branch_unlikely(&init_on_free)) &&
|
||||||
|
@ -1352,36 +1416,47 @@ slab_flags_t kmem_cache_flags(unsigned int object_size,
|
||||||
{
|
{
|
||||||
char *iter;
|
char *iter;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
char *next_block;
|
||||||
|
slab_flags_t block_flags;
|
||||||
|
|
||||||
/* If slub_debug = 0, it folds into the if conditional. */
|
/* If slub_debug = 0, it folds into the if conditional. */
|
||||||
if (!slub_debug_slabs)
|
if (!slub_debug_string)
|
||||||
return flags | slub_debug;
|
return flags | slub_debug;
|
||||||
|
|
||||||
len = strlen(name);
|
len = strlen(name);
|
||||||
iter = slub_debug_slabs;
|
next_block = slub_debug_string;
|
||||||
while (*iter) {
|
/* Go through all blocks of debug options, see if any matches our slab's name */
|
||||||
char *end, *glob;
|
while (next_block) {
|
||||||
size_t cmplen;
|
next_block = parse_slub_debug_flags(next_block, &block_flags, &iter, false);
|
||||||
|
if (!iter)
|
||||||
|
continue;
|
||||||
|
/* Found a block that has a slab list, search it */
|
||||||
|
while (*iter) {
|
||||||
|
char *end, *glob;
|
||||||
|
size_t cmplen;
|
||||||
|
|
||||||
end = strchrnul(iter, ',');
|
end = strchrnul(iter, ',');
|
||||||
|
if (next_block && next_block < end)
|
||||||
|
end = next_block - 1;
|
||||||
|
|
||||||
glob = strnchr(iter, end - iter, '*');
|
glob = strnchr(iter, end - iter, '*');
|
||||||
if (glob)
|
if (glob)
|
||||||
cmplen = glob - iter;
|
cmplen = glob - iter;
|
||||||
else
|
else
|
||||||
cmplen = max_t(size_t, len, (end - iter));
|
cmplen = max_t(size_t, len, (end - iter));
|
||||||
|
|
||||||
if (!strncmp(name, iter, cmplen)) {
|
if (!strncmp(name, iter, cmplen)) {
|
||||||
flags |= slub_debug;
|
flags |= block_flags;
|
||||||
break;
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*end || *end == ';')
|
||||||
|
break;
|
||||||
|
iter = end + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*end)
|
|
||||||
break;
|
|
||||||
iter = end + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return flags;
|
return slub_debug;
|
||||||
}
|
}
|
||||||
#else /* !CONFIG_SLUB_DEBUG */
|
#else /* !CONFIG_SLUB_DEBUG */
|
||||||
static inline void setup_object_debug(struct kmem_cache *s,
|
static inline void setup_object_debug(struct kmem_cache *s,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче