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

s390/debug: keep debug data on resize



Any previously recorded s390dbf debug data is reset when a debug area
is resized using the 'pages' sysfs attribute. This can make
live-debugging unnecessarily complex.

Fix this by copying existing debug data to the newly allocated debug
area when resizing.

Signed-off-by: default avatarPeter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parent 2879048c
Loading
Loading
Loading
Loading
+53 −21
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/export.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/minmax.h>
#include <linux/debugfs.h>

#include <asm/debug.h>
@@ -92,6 +93,8 @@ static int debug_hex_ascii_format_fn(debug_info_t *id, struct debug_view *view,
				     char *out_buf, const char *in_buf);
static int debug_sprintf_format_fn(debug_info_t *id, struct debug_view *view,
				   char *out_buf, debug_sprintf_entry_t *curr_event);
static void debug_areas_swap(debug_info_t *a, debug_info_t *b);
static void debug_events_append(debug_info_t *dest, debug_info_t *src);

/* globals */

@@ -726,35 +729,28 @@ EXPORT_SYMBOL(debug_unregister);
 */
static int debug_set_size(debug_info_t *id, int nr_areas, int pages_per_area)
{
	debug_entry_t ***new_areas;
	debug_info_t *new_id;
	unsigned long flags;
	int rc = 0;

	if (!id || (nr_areas <= 0) || (pages_per_area < 0))
		return -EINVAL;
	if (pages_per_area > 0) {
		new_areas = debug_areas_alloc(pages_per_area, nr_areas);
		if (!new_areas) {

	new_id = debug_info_alloc("", pages_per_area, nr_areas, id->buf_size,
				  id->level, ALL_AREAS);
	if (!new_id) {
		pr_info("Allocating memory for %i pages failed\n",
			pages_per_area);
			rc = -ENOMEM;
			goto out;
		}
	} else {
		new_areas = NULL;
		return -ENOMEM;
	}

	spin_lock_irqsave(&id->lock, flags);
	debug_areas_free(id);
	id->areas = new_areas;
	id->nr_areas = nr_areas;
	id->pages_per_area = pages_per_area;
	id->active_area = 0;
	memset(id->active_entries, 0, sizeof(int)*id->nr_areas);
	memset(id->active_pages, 0, sizeof(int)*id->nr_areas);
	debug_events_append(new_id, id);
	debug_areas_swap(new_id, id);
	debug_info_free(new_id);
	spin_unlock_irqrestore(&id->lock, flags);
	pr_info("%s: set new size (%i pages)\n", id->name, pages_per_area);
out:
	return rc;

	return 0;
}

/**
@@ -821,6 +817,42 @@ static inline debug_entry_t *get_active_entry(debug_info_t *id)
				  id->active_entries[id->active_area]);
}

/* Swap debug areas of a and b. */
static void debug_areas_swap(debug_info_t *a, debug_info_t *b)
{
	swap(a->nr_areas, b->nr_areas);
	swap(a->pages_per_area, b->pages_per_area);
	swap(a->areas, b->areas);
	swap(a->active_area, b->active_area);
	swap(a->active_pages, b->active_pages);
	swap(a->active_entries, b->active_entries);
}

/* Append all debug events in active area from source to destination log. */
static void debug_events_append(debug_info_t *dest, debug_info_t *src)
{
	debug_entry_t *from, *to, *last;

	if (!src->areas || !dest->areas)
		return;

	/* Loop over all entries in src, starting with oldest. */
	from = get_active_entry(src);
	last = from;
	do {
		if (from->clock != 0LL) {
			to = get_active_entry(dest);
			memset(to, 0, dest->entry_size);
			memcpy(to, from, min(src->entry_size,
					     dest->entry_size));
			proceed_active_entry(dest);
		}

		proceed_active_entry(src);
		from = get_active_entry(src);
	} while (from != last);
}

/*
 * debug_finish_entry:
 * - set timestamp, caller address, cpu number etc.