s390/debug: add early tracing support

Debug areas can currently only be used after s390dbf initialization
which occurs as a postcore_initcall. This is too late for tracing
earlier code such as that related to console_init().

This patch introduces a macro for defining a statically initialized
debug area that can be used to trace very early code. The macro is made
available for built-in code only because modules are never running
during early boot.

Example usage:

1. Define static debug area:

  DEFINE_STATIC_DEBUG_INFO(my_debug, "my_debug", 4, 1, 16,
			   &debug_hex_ascii_view);

2. Add trace entry:

  debug_event(&my_debug, 0, "DATA", 4);

Note: The debug area is automatically registered in debugfs during boot.
      A driver must not call any of the debug_register()/_unregister()
      functions on a static debug_info_t!

Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
This commit is contained in:
Peter Oberparleiter 2021-08-13 15:05:04 +02:00 committed by Heiko Carstens
parent 9372a82892
commit d72541f945
2 changed files with 151 additions and 0 deletions

View File

@ -13,6 +13,7 @@
#include <linux/time.h> #include <linux/time.h>
#include <linux/refcount.h> #include <linux/refcount.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/init.h>
#define DEBUG_MAX_LEVEL 6 /* debug levels range from 0 to 6 */ #define DEBUG_MAX_LEVEL 6 /* debug levels range from 0 to 6 */
#define DEBUG_OFF_LEVEL -1 /* level where debug is switched off */ #define DEBUG_OFF_LEVEL -1 /* level where debug is switched off */
@ -391,4 +392,99 @@ int debug_register_view(debug_info_t *id, struct debug_view *view);
int debug_unregister_view(debug_info_t *id, struct debug_view *view); int debug_unregister_view(debug_info_t *id, struct debug_view *view);
#ifndef MODULE
/*
* Note: Initial page and area numbers must be fixed to allow static
* initialization. This enables very early tracing. Changes to these values
* must be reflected in __DEFINE_STATIC_AREA.
*/
#define EARLY_PAGES 8
#define EARLY_AREAS 1
#define VNAME(var, suffix) __##var##_##suffix
/*
* Define static areas for early trace data. During boot debug_register_static()
* will replace these with dynamically allocated areas to allow custom page and
* area sizes, and dynamic resizing.
*/
#define __DEFINE_STATIC_AREA(var) \
static char VNAME(var, data)[EARLY_PAGES][PAGE_SIZE] __initdata; \
static debug_entry_t *VNAME(var, pages)[EARLY_PAGES] __initdata = { \
(debug_entry_t *)VNAME(var, data)[0], \
(debug_entry_t *)VNAME(var, data)[1], \
(debug_entry_t *)VNAME(var, data)[2], \
(debug_entry_t *)VNAME(var, data)[3], \
(debug_entry_t *)VNAME(var, data)[4], \
(debug_entry_t *)VNAME(var, data)[5], \
(debug_entry_t *)VNAME(var, data)[6], \
(debug_entry_t *)VNAME(var, data)[7], \
}; \
static debug_entry_t **VNAME(var, areas)[EARLY_AREAS] __initdata = { \
(debug_entry_t **)VNAME(var, pages), \
}; \
static int VNAME(var, active_pages)[EARLY_AREAS] __initdata; \
static int VNAME(var, active_entries)[EARLY_AREAS] __initdata
#define __DEBUG_INFO_INIT(var, _name, _buf_size) { \
.next = NULL, \
.prev = NULL, \
.ref_count = REFCOUNT_INIT(1), \
.lock = __SPIN_LOCK_UNLOCKED(var.lock), \
.level = DEBUG_DEFAULT_LEVEL, \
.nr_areas = EARLY_AREAS, \
.pages_per_area = EARLY_PAGES, \
.buf_size = (_buf_size), \
.entry_size = sizeof(debug_entry_t) + (_buf_size), \
.areas = VNAME(var, areas), \
.active_area = 0, \
.active_pages = VNAME(var, active_pages), \
.active_entries = VNAME(var, active_entries), \
.debugfs_root_entry = NULL, \
.debugfs_entries = { NULL }, \
.views = { NULL }, \
.name = (_name), \
.mode = 0600, \
}
#define __REGISTER_STATIC_DEBUG_INFO(var, name, pages, areas, view) \
static int __init VNAME(var, reg)(void) \
{ \
debug_register_static(&var, (pages), (areas)); \
debug_register_view(&var, (view)); \
return 0; \
} \
arch_initcall(VNAME(var, reg))
/**
* DEFINE_STATIC_DEBUG_INFO - Define static debug_info_t
*
* @var: Name of debug_info_t variable
* @name: Name of debug log (e.g. used for debugfs entry)
* @pages_per_area: Number of pages per area
* @nr_areas: Number of debug areas
* @buf_size: Size of data area in each debug entry
* @view: Pointer to debug view struct
*
* Define a static debug_info_t for early tracing. The associated debugfs log
* is automatically registered with the specified debug view.
*
* Important: Users of this macro must not call any of the
* debug_register/_unregister() functions for this debug_info_t!
*
* Note: Tracing will start with a fixed number of initial pages and areas.
* The debug area will be changed to use the specified numbers during
* arch_initcall.
*/
#define DEFINE_STATIC_DEBUG_INFO(var, name, pages, nr_areas, buf_size, view) \
__DEFINE_STATIC_AREA(var); \
static debug_info_t __refdata var = \
__DEBUG_INFO_INIT(var, (name), (buf_size)); \
__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);
#endif /* MODULE */
#endif /* DEBUG_H */ #endif /* DEBUG_H */

View File

@ -692,6 +692,61 @@ debug_info_t *debug_register(const char *name, int pages_per_area,
} }
EXPORT_SYMBOL(debug_register); EXPORT_SYMBOL(debug_register);
/**
* debug_register_static() - registers a static debug area
*
* @id: Handle for static debug area
* @pages_per_area: Number of pages per area
* @nr_areas: Number of debug areas
*
* Register debug_info_t defined using DEFINE_STATIC_DEBUG_INFO.
*
* Note: This function is called automatically via an initcall generated by
* DEFINE_STATIC_DEBUG_INFO.
*/
void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
{
unsigned long flags;
debug_info_t *copy;
if (!initialized) {
pr_err("Tried to register debug feature %s too early\n",
id->name);
return;
}
copy = debug_info_alloc("", pages_per_area, nr_areas, id->buf_size,
id->level, ALL_AREAS);
if (!copy) {
pr_err("Registering debug feature %s failed\n", id->name);
/* Clear pointers to prevent tracing into released initdata. */
spin_lock_irqsave(&id->lock, flags);
id->areas = NULL;
id->active_pages = NULL;
id->active_entries = NULL;
spin_unlock_irqrestore(&id->lock, flags);
return;
}
/* Replace static trace area with dynamic copy. */
spin_lock_irqsave(&id->lock, flags);
debug_events_append(copy, id);
debug_areas_swap(id, copy);
spin_unlock_irqrestore(&id->lock, flags);
/* Clear pointers to initdata and discard copy. */
copy->areas = NULL;
copy->active_pages = NULL;
copy->active_entries = NULL;
debug_info_free(copy);
mutex_lock(&debug_mutex);
_debug_register(id);
mutex_unlock(&debug_mutex);
}
/* Remove debugfs entries and remove from internal list. */ /* Remove debugfs entries and remove from internal list. */
static void _debug_unregister(debug_info_t *id) static void _debug_unregister(debug_info_t *id)
{ {