s390/debug: Add s390dbf kernel parameter

Problem determination using s390dbf logging sometimes requires changing
the default logging level or log area size. While this is possible
using sysfs interfaces, there is no easy way to adjust these parameters
for early boot code that emits logs before userspace is available.

Add an s390dbf kernel parameter to address this shortcoming. The
parameter can be used to specify log level and area size (in units of
pages). A level of '-' turns logging off for an area. Logs can be
identified by name or a shell-style pattern.

Parameter format:

  s390dbf=<name|pattern>:[<level>|-]:[<pages>][,...]

Example:

  s390dbf=cio*:6:128,sclp_err::2

Specified parameters are applied immediately during debug area
registration for regular log areas. For early, static debug areas,
log levels are changed during early_param() parsing, while size
changes are applied at arch_initcall-time.

Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Tested-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
This commit is contained in:
Peter Oberparleiter 2026-05-06 16:53:27 +02:00 committed by Alexander Gordeev
parent 4179c39842
commit a2cec68637
5 changed files with 150 additions and 14 deletions

View File

@ -106,6 +106,36 @@ the ``debug_stoppable`` sysctl. If you set ``debug_stoppable`` to 0 the debug
feature cannot be stopped. If the debug feature is already stopped, it
will stay deactivated.
Kernel parameters
-----------------
The size and log levels of debug logs can be configured early during boot using
the ``s390dbf`` kernel parameter. The parameter accepts a debug log name, a log
level, and a log size, separated by colon characters (``:``). To configure only
a single attribute, either the log level or the log size may be omitted.
To configure multiple debug logs, the parameter may be specified multiple times,
or multiple parameter sets may be provided in a single instance, separated by
commas.
Parameter format::
s390dbf=<name|pattern>:[<level>|-]:[<pages>][,...]
where:
- ``name`` specifies either an exact debug log name or a shell-style wildcard
pattern
- ``level`` specifies the log level, or ``-`` to completely deactivate the log
- ``pages`` specifies the debug area size in pages
Example::
s390dbf=cio*:6:128,sclp_err::2
This example sets the log level to 6 and the log size to 128 pages for all debug
logs whose names start with ``cio``. It also sets the log level of the
``sclp_err`` debug log to 2.
Kernel Interfaces:
------------------

View File

@ -179,6 +179,7 @@ config S390
select GENERIC_SMP_IDLE_THREAD
select GENERIC_TIME_VSYSCALL
select GENERIC_IOREMAP if PCI
select GLOB
select HAVE_ALIGNED_STRUCT_PAGE
select HAVE_ARCH_AUDITSYSCALL
select HAVE_ARCH_GET_SECUREBOOT

View File

@ -490,6 +490,7 @@ arch_initcall(VNAME(var, reg))
__DEFINE_STATIC_AREA(var); \
static debug_info_t __refdata var = \
__DEBUG_INFO_INIT(var, (name), (buf_size)); \
static debug_info_t __used __section(".s390dbf_info") *VNAME(var, info) = &var; \
__REGISTER_STATIC_DEBUG_INFO(var, name, pages, nr_areas, view)
void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas);

View File

@ -26,6 +26,8 @@
#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/debugfs.h>
#include <linux/glob.h>
#include <linux/stringify.h>
#include <asm/debug.h>
@ -154,6 +156,7 @@ static unsigned int __used debug_feature_version = __DEBUG_FEATURE_VERSION;
static debug_info_t *debug_area_first;
static debug_info_t *debug_area_last;
static DEFINE_MUTEX(debug_mutex);
extern debug_info_t *__s390dbf_info[], *__s390dbf_info_end[];
static int initialized;
static int debug_critical;
@ -168,7 +171,91 @@ static const struct file_operations debug_file_ops = {
static struct dentry *debug_debugfs_root_entry;
/* List of debug area parameters to override */
#define PARAM_UNSET -2
#define PARAM_NUM 16
static struct debug_param_t {
char name[DEBUG_MAX_NAME_LEN + 1];
int level;
int pages;
} debug_param[PARAM_NUM];
static int debug_param_num;
/* functions */
static void debug_get_param(const char *name, int *level, int *pages)
{
struct debug_param_t *p;
int i;
for (i = 0; i < debug_param_num; i++) {
p = &debug_param[i];
if (!glob_match(p->name, name))
continue;
if (level && p->level != PARAM_UNSET) {
pr_info("%s: override level to %d\n", name, p->level);
*level = p->level;
}
if (pages && p->pages != PARAM_UNSET) {
pr_info("%s: override pages to %d\n", name, p->pages);
*pages = p->pages;
}
}
}
#define LVL_LEN 10
#define LVL_FMT "%" __stringify(LVL_LEN) "[^:]"
#define NAME_FMT "%" __stringify(DEBUG_MAX_NAME_LEN) "[^:]"
static bool __init s390dbf_parse_one(const char *arg, struct debug_param_t *param)
{
struct debug_param_t p = { { 0 }, PARAM_UNSET, PARAM_UNSET };
char level[LVL_LEN + 1] = { 0 };
/* arg: <name|pattern>:[<level>|-]:[<pages>] */
if (sscanf(arg, NAME_FMT ":" LVL_FMT ":%d", p.name, level, &p.pages) > 1) {
if (strcmp(level, "-") == 0)
p.level = DEBUG_OFF_LEVEL;
else if (kstrtoint(level, 0, &p.level) != 0)
return false;
} else if (sscanf(arg, NAME_FMT "::%d", p.name, &p.pages) != 2) {
return false;
}
if (p.level != PARAM_UNSET && p.level != DEBUG_OFF_LEVEL &&
(p.level < 0 || p.level > DEBUG_MAX_LEVEL))
return false;
if (p.pages != PARAM_UNSET && p.pages < 0)
return false;
*param = p;
return true;
}
static int __init s390dbf_parse(char *arg)
{
debug_info_t **id;
int i, rc = 0;
while (arg && debug_param_num < PARAM_NUM) {
if (s390dbf_parse_one(arg, &debug_param[debug_param_num]))
debug_param_num++;
else
rc = -EINVAL;
arg = strchr(arg, ',');
if (arg)
arg++;
}
/*
* Apply level to static debug areas, delay buffer size changes until
* regular memory allocations are possible.
*/
for (i = 0, id = __s390dbf_info; &id[i] < __s390dbf_info_end; i++)
debug_get_param(id[i]->name, &id[i]->level, NULL);
return rc;
}
early_param("s390dbf", s390dbf_parse);
/*
* debug_areas_alloc
@ -305,10 +392,11 @@ static void debug_info_free(debug_info_t *db_info)
static debug_info_t *debug_info_create(const char *name, int pages_per_area,
int nr_areas, int buf_size, umode_t mode)
{
int level = DEBUG_DEFAULT_LEVEL;
debug_info_t *rc;
rc = debug_info_alloc(name, pages_per_area, nr_areas, buf_size,
DEBUG_DEFAULT_LEVEL, ALL_AREAS);
debug_get_param(name, &level, &pages_per_area);
rc = debug_info_alloc(name, pages_per_area, nr_areas, buf_size, level, ALL_AREAS);
if (!rc)
goto out;
@ -872,6 +960,7 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
return;
}
debug_get_param(id->name, &id->level, &pages_per_area);
copy = debug_info_alloc("", pages_per_area, nr_areas, id->buf_size,
id->level, ALL_AREAS);
if (!copy) {
@ -975,16 +1064,7 @@ static int debug_set_size(debug_info_t *id, int nr_areas, int pages_per_area)
return 0;
}
/**
* debug_set_level() - Sets new actual debug level if new_level is valid.
*
* @id: handle for debug log
* @new_level: new debug level
*
* Return:
* none
*/
void debug_set_level(debug_info_t *id, int new_level)
static void _debug_set_level(debug_info_t *id, int new_level)
{
unsigned long flags;
@ -1003,6 +1083,23 @@ void debug_set_level(debug_info_t *id, int new_level)
id->level = new_level;
raw_spin_unlock_irqrestore(&id->lock, flags);
}
/**
* debug_set_level() - Sets new actual debug level if new_level is valid.
*
* @id: handle for debug log
* @new_level: new debug level
*
* Return:
* none
*/
void debug_set_level(debug_info_t *id, int new_level)
{
/* Level specified via kernel parameter takes precedence */
debug_get_param(id->name, &new_level, NULL);
_debug_set_level(id, new_level);
}
EXPORT_SYMBOL(debug_set_level);
/*
@ -1529,7 +1626,7 @@ static int debug_input_level_fn(debug_info_t *id, struct debug_view *view,
goto out;
}
if (str[0] == '-') {
debug_set_level(id, DEBUG_OFF_LEVEL);
_debug_set_level(id, DEBUG_OFF_LEVEL);
rc = user_len;
goto free_str;
} else {
@ -1539,7 +1636,7 @@ static int debug_input_level_fn(debug_info_t *id, struct debug_view *view,
pr_warn("%s is not a valid level for a debug feature\n", str);
rc = -EINVAL;
} else {
debug_set_level(id, new_level);
_debug_set_level(id, new_level);
rc = user_len;
}
free_str:

View File

@ -159,6 +159,13 @@ SECTIONS
}
#endif
. = ALIGN(8);
.s390dbf_info : {
__s390dbf_info = .;
*(.s390dbf_info)
__s390dbf_info_end = .;
}
/*
* Table with the patch locations to undo expolines
*/