Commit d72541f9 authored by Peter Oberparleiter's avatar Peter Oberparleiter Committed by Heiko Carstens
Browse files

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: default avatarPeter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parent 9372a828
Loading
Loading
Loading
Loading
+96 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/time.h>
#include <linux/refcount.h>
#include <linux/fs.h>
#include <linux/init.h>

#define DEBUG_MAX_LEVEL		   6  /* debug levels range from 0 to 6 */
#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);

#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 */
+55 −0
Original line number Diff line number Diff line
@@ -692,6 +692,61 @@ debug_info_t *debug_register(const char *name, int pages_per_area,
}
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. */
static void _debug_unregister(debug_info_t *id)
{