From: Peter Oberparleiter Date: Wed, 6 May 2026 14:53:27 +0000 (+0200) Subject: s390/debug: Add s390dbf kernel parameter X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a2cec6863709d6673a025acc066c962b85a75842;p=thirdparty%2Fkernel%2Flinux.git 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=:[|-]:[][,...] 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 Tested-by: Vineeth Vijayan Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- diff --git a/Documentation/arch/s390/s390dbf.rst b/Documentation/arch/s390/s390dbf.rst index aad6d88974fe5..034374c88dbaf 100644 --- a/Documentation/arch/s390/s390dbf.rst +++ b/Documentation/arch/s390/s390dbf.rst @@ -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=:[|-]:[][,...] + +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: ------------------ diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index ecbcbb781e40a..301cdc9f73aff 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -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 diff --git a/arch/s390/include/asm/debug.h b/arch/s390/include/asm/debug.h index c5440b3ee53d7..39d484c597748 100644 --- a/arch/s390/include/asm/debug.h +++ b/arch/s390/include/asm/debug.h @@ -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); diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index 7650f2adb5cf8..7bc403d081fac 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include @@ -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: :[|-]:[] */ + 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: diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index 2b62395e35bfb..1f0e71c58eb9c 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -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 */