xref: /linux-6.15/arch/s390/kernel/debug.c (revision 1751f872)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *   S/390 debug facility
41da177e4SLinus Torvalds  *
50990d836SMikhail Zaslonko  *    Copyright IBM Corp. 1999, 2020
63ab121abSMichael Holzheu  *
71da177e4SLinus Torvalds  *    Author(s): Michael Holzheu ([email protected]),
81da177e4SLinus Torvalds  *		 Holger Smolinski ([email protected])
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *    Bugreports to: <[email protected]>
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
13c5612c19SMichael Holzheu #define KMSG_COMPONENT "s390dbf"
14c5612c19SMichael Holzheu #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
15c5612c19SMichael Holzheu 
161da177e4SLinus Torvalds #include <linux/stddef.h>
171da177e4SLinus Torvalds #include <linux/kernel.h>
181da177e4SLinus Torvalds #include <linux/errno.h>
191da177e4SLinus Torvalds #include <linux/slab.h>
201da177e4SLinus Torvalds #include <linux/ctype.h>
21e7d2860bSAndré Goddard Rosa #include <linux/string.h>
221da177e4SLinus Torvalds #include <linux/sysctl.h>
237c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
243994a52bSPaul Gortmaker #include <linux/export.h>
251da177e4SLinus Torvalds #include <linux/init.h>
2666a464dbSMichael Holzheu #include <linux/fs.h>
27dc18c81aSNiklas Schnelle #include <linux/math.h>
2812047778SPeter Oberparleiter #include <linux/minmax.h>
2966a464dbSMichael Holzheu #include <linux/debugfs.h>
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds #include <asm/debug.h>
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds #define DEBUG_PROLOG_ENTRY -1
341da177e4SLinus Torvalds 
3566a464dbSMichael Holzheu #define ALL_AREAS 0 /* copy all debug areas */
3666a464dbSMichael Holzheu #define NO_AREAS  1 /* copy no debug areas */
3766a464dbSMichael Holzheu 
381da177e4SLinus Torvalds /* typedefs */
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds typedef struct file_private_info {
411da177e4SLinus Torvalds 	loff_t offset;			/* offset of last read in file */
42e6ebf0d6SHeiko Carstens 	int    act_area;		/* number of last formatted area */
4366a464dbSMichael Holzheu 	int    act_page;		/* act page in given area */
44e6ebf0d6SHeiko Carstens 	int    act_entry;		/* last formatted entry (offset */
451da177e4SLinus Torvalds 					/* relative to beginning of last */
46e6ebf0d6SHeiko Carstens 					/* formatted page) */
471da177e4SLinus Torvalds 	size_t act_entry_offset;	/* up to this offset we copied */
48e6ebf0d6SHeiko Carstens 					/* in last read the last formatted */
491da177e4SLinus Torvalds 					/* entry to userland */
501da177e4SLinus Torvalds 	char   temp_buf[2048];		/* buffer for output */
511da177e4SLinus Torvalds 	debug_info_t *debug_info_org;	/* original debug information */
521da177e4SLinus Torvalds 	debug_info_t *debug_info_snap;	/* snapshot of debug information */
531da177e4SLinus Torvalds 	struct debug_view *view;	/* used view of debug info */
541da177e4SLinus Torvalds } file_private_info_t;
551da177e4SLinus Torvalds 
56496da0d7SHeiko Carstens typedef struct {
571da177e4SLinus Torvalds 	char *string;
581da177e4SLinus Torvalds 	/*
591da177e4SLinus Torvalds 	 * This assumes that all args are converted into longs
601da177e4SLinus Torvalds 	 * on L/390 this is the case for all types of parameter
611da177e4SLinus Torvalds 	 * except of floats, and long long (32 bit)
621da177e4SLinus Torvalds 	 *
631da177e4SLinus Torvalds 	 */
64ca1382eaSHeiko Carstens 	long args[];
651da177e4SLinus Torvalds } debug_sprintf_entry_t;
661da177e4SLinus Torvalds 
67e6ebf0d6SHeiko Carstens /* internal function prototypes */
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds static int debug_init(void);
701da177e4SLinus Torvalds static ssize_t debug_output(struct file *file, char __user *user_buf,
711da177e4SLinus Torvalds 			    size_t user_len, loff_t *offset);
721da177e4SLinus Torvalds static ssize_t debug_input(struct file *file, const char __user *user_buf,
731da177e4SLinus Torvalds 			   size_t user_len, loff_t *offset);
741da177e4SLinus Torvalds static int debug_open(struct inode *inode, struct file *file);
751da177e4SLinus Torvalds static int debug_close(struct inode *inode, struct file *file);
765cbbf16aSCornelia Huck static debug_info_t *debug_info_create(const char *name, int pages_per_area,
77f4ae40a6SAl Viro 				       int nr_areas, int buf_size, umode_t mode);
781da177e4SLinus Torvalds static void debug_info_get(debug_info_t *);
791da177e4SLinus Torvalds static void debug_info_put(debug_info_t *);
801da177e4SLinus Torvalds static int debug_prolog_level_fn(debug_info_t *id,
81897614f9SNiklas Schnelle 				 struct debug_view *view, char *out_buf,
82897614f9SNiklas Schnelle 				 size_t out_buf_size);
831da177e4SLinus Torvalds static int debug_input_level_fn(debug_info_t *id, struct debug_view *view,
841da177e4SLinus Torvalds 				struct file *file, const char __user *user_buf,
851da177e4SLinus Torvalds 				size_t user_buf_size, loff_t *offset);
8666a464dbSMichael Holzheu static int debug_prolog_pages_fn(debug_info_t *id,
87897614f9SNiklas Schnelle 				 struct debug_view *view, char *out_buf,
88897614f9SNiklas Schnelle 				 size_t out_buf_size);
8966a464dbSMichael Holzheu static int debug_input_pages_fn(debug_info_t *id, struct debug_view *view,
9066a464dbSMichael Holzheu 				struct file *file, const char __user *user_buf,
9166a464dbSMichael Holzheu 				size_t user_buf_size, loff_t *offset);
921da177e4SLinus Torvalds static int debug_input_flush_fn(debug_info_t *id, struct debug_view *view,
931da177e4SLinus Torvalds 				struct file *file, const char __user *user_buf,
941da177e4SLinus Torvalds 				size_t user_buf_size, loff_t *offset);
951da177e4SLinus Torvalds static int debug_hex_ascii_format_fn(debug_info_t *id, struct debug_view *view,
96897614f9SNiklas Schnelle 				     char *out_buf, size_t out_buf_size,
97897614f9SNiklas Schnelle 				     const char *in_buf);
9812047778SPeter Oberparleiter static void debug_areas_swap(debug_info_t *a, debug_info_t *b);
9912047778SPeter Oberparleiter static void debug_events_append(debug_info_t *dest, debug_info_t *src);
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds /* globals */
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds struct debug_view debug_hex_ascii_view = {
1041da177e4SLinus Torvalds 	"hex_ascii",
1051da177e4SLinus Torvalds 	NULL,
1061da177e4SLinus Torvalds 	&debug_dflt_header_fn,
1071da177e4SLinus Torvalds 	&debug_hex_ascii_format_fn,
1081da177e4SLinus Torvalds 	NULL,
1091da177e4SLinus Torvalds 	NULL
1101da177e4SLinus Torvalds };
111d5274369SHeiko Carstens EXPORT_SYMBOL(debug_hex_ascii_view);
1121da177e4SLinus Torvalds 
1132b67fc46SHeiko Carstens static struct debug_view debug_level_view = {
1141da177e4SLinus Torvalds 	"level",
1151da177e4SLinus Torvalds 	&debug_prolog_level_fn,
1161da177e4SLinus Torvalds 	NULL,
1171da177e4SLinus Torvalds 	NULL,
1181da177e4SLinus Torvalds 	&debug_input_level_fn,
1191da177e4SLinus Torvalds 	NULL
1201da177e4SLinus Torvalds };
1211da177e4SLinus Torvalds 
1222b67fc46SHeiko Carstens static struct debug_view debug_pages_view = {
12366a464dbSMichael Holzheu 	"pages",
12466a464dbSMichael Holzheu 	&debug_prolog_pages_fn,
12566a464dbSMichael Holzheu 	NULL,
12666a464dbSMichael Holzheu 	NULL,
12766a464dbSMichael Holzheu 	&debug_input_pages_fn,
12866a464dbSMichael Holzheu 	NULL
12966a464dbSMichael Holzheu };
13066a464dbSMichael Holzheu 
1312b67fc46SHeiko Carstens static struct debug_view debug_flush_view = {
1321da177e4SLinus Torvalds 	"flush",
1331da177e4SLinus Torvalds 	NULL,
1341da177e4SLinus Torvalds 	NULL,
1351da177e4SLinus Torvalds 	NULL,
1361da177e4SLinus Torvalds 	&debug_input_flush_fn,
1371da177e4SLinus Torvalds 	NULL
1381da177e4SLinus Torvalds };
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds struct debug_view debug_sprintf_view = {
1411da177e4SLinus Torvalds 	"sprintf",
1421da177e4SLinus Torvalds 	NULL,
1431da177e4SLinus Torvalds 	&debug_dflt_header_fn,
144a78c2e31SHeiko Carstens 	&debug_sprintf_format_fn,
1451da177e4SLinus Torvalds 	NULL,
1461da177e4SLinus Torvalds 	NULL
1471da177e4SLinus Torvalds };
148d5274369SHeiko Carstens EXPORT_SYMBOL(debug_sprintf_view);
1491da177e4SLinus Torvalds 
1502b67fc46SHeiko Carstens /* used by dump analysis tools to determine version of debug feature */
151a806170eSHeiko Carstens static unsigned int __used debug_feature_version = __DEBUG_FEATURE_VERSION;
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds /* static globals */
1541da177e4SLinus Torvalds 
155496da0d7SHeiko Carstens static debug_info_t *debug_area_first;
156496da0d7SHeiko Carstens static debug_info_t *debug_area_last;
157e11f0d04SChristoph Hellwig static DEFINE_MUTEX(debug_mutex);
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds static int initialized;
1603ab121abSMichael Holzheu static int debug_critical;
1611da177e4SLinus Torvalds 
1625dfe4c96SArjan van de Ven static const struct file_operations debug_file_ops = {
1631da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
1641da177e4SLinus Torvalds 	.read	 = debug_output,
1651da177e4SLinus Torvalds 	.write	 = debug_input,
1661da177e4SLinus Torvalds 	.open	 = debug_open,
1671da177e4SLinus Torvalds 	.release = debug_close,
1681da177e4SLinus Torvalds };
1691da177e4SLinus Torvalds 
17066a464dbSMichael Holzheu static struct dentry *debug_debugfs_root_entry;
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds /* functions */
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds /*
17566a464dbSMichael Holzheu  * debug_areas_alloc
17666a464dbSMichael Holzheu  * - Debug areas are implemented as a threedimensonal array:
17766a464dbSMichael Holzheu  *   areas[areanumber][pagenumber][pageoffset]
17866a464dbSMichael Holzheu  */
17966a464dbSMichael Holzheu 
debug_areas_alloc(int pages_per_area,int nr_areas)180496da0d7SHeiko Carstens static debug_entry_t ***debug_areas_alloc(int pages_per_area, int nr_areas)
18166a464dbSMichael Holzheu {
18266a464dbSMichael Holzheu 	debug_entry_t ***areas;
18366a464dbSMichael Holzheu 	int i, j;
18466a464dbSMichael Holzheu 
1856da2ec56SKees Cook 	areas = kmalloc_array(nr_areas, sizeof(debug_entry_t **), GFP_KERNEL);
18666a464dbSMichael Holzheu 	if (!areas)
18766a464dbSMichael Holzheu 		goto fail_malloc_areas;
18866a464dbSMichael Holzheu 	for (i = 0; i < nr_areas; i++) {
189827c4913SChristian Borntraeger 		/* GFP_NOWARN to avoid user triggerable WARN, we handle fails */
1906da2ec56SKees Cook 		areas[i] = kmalloc_array(pages_per_area,
1916da2ec56SKees Cook 					 sizeof(debug_entry_t *),
192827c4913SChristian Borntraeger 					 GFP_KERNEL | __GFP_NOWARN);
193496da0d7SHeiko Carstens 		if (!areas[i])
19466a464dbSMichael Holzheu 			goto fail_malloc_areas2;
19566a464dbSMichael Holzheu 		for (j = 0; j < pages_per_area; j++) {
196fb630517SEric Sesterhenn 			areas[i][j] = kzalloc(PAGE_SIZE, GFP_KERNEL);
19766a464dbSMichael Holzheu 			if (!areas[i][j]) {
198496da0d7SHeiko Carstens 				for (j--; j >= 0 ; j--)
19966a464dbSMichael Holzheu 					kfree(areas[i][j]);
20066a464dbSMichael Holzheu 				kfree(areas[i]);
20166a464dbSMichael Holzheu 				goto fail_malloc_areas2;
20266a464dbSMichael Holzheu 			}
20366a464dbSMichael Holzheu 		}
20466a464dbSMichael Holzheu 	}
20566a464dbSMichael Holzheu 	return areas;
20666a464dbSMichael Holzheu 
20766a464dbSMichael Holzheu fail_malloc_areas2:
20866a464dbSMichael Holzheu 	for (i--; i >= 0; i--) {
209496da0d7SHeiko Carstens 		for (j = 0; j < pages_per_area; j++)
21066a464dbSMichael Holzheu 			kfree(areas[i][j]);
21166a464dbSMichael Holzheu 		kfree(areas[i]);
21266a464dbSMichael Holzheu 	}
21366a464dbSMichael Holzheu 	kfree(areas);
21466a464dbSMichael Holzheu fail_malloc_areas:
21566a464dbSMichael Holzheu 	return NULL;
21666a464dbSMichael Holzheu }
21766a464dbSMichael Holzheu 
21866a464dbSMichael Holzheu /*
2191da177e4SLinus Torvalds  * debug_info_alloc
2201da177e4SLinus Torvalds  * - alloc new debug-info
2211da177e4SLinus Torvalds  */
debug_info_alloc(const char * name,int pages_per_area,int nr_areas,int buf_size,int level,int mode)222496da0d7SHeiko Carstens static debug_info_t *debug_info_alloc(const char *name, int pages_per_area,
223496da0d7SHeiko Carstens 				      int nr_areas, int buf_size, int level,
224496da0d7SHeiko Carstens 				      int mode)
2251da177e4SLinus Torvalds {
2261da177e4SLinus Torvalds 	debug_info_t *rc;
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	/* alloc everything */
2295cbded58SRobert P. J. Day 	rc = kmalloc(sizeof(debug_info_t), GFP_KERNEL);
2301da177e4SLinus Torvalds 	if (!rc)
2311da177e4SLinus Torvalds 		goto fail_malloc_rc;
232fb630517SEric Sesterhenn 	rc->active_entries = kcalloc(nr_areas, sizeof(int), GFP_KERNEL);
23366a464dbSMichael Holzheu 	if (!rc->active_entries)
23466a464dbSMichael Holzheu 		goto fail_malloc_active_entries;
235fb630517SEric Sesterhenn 	rc->active_pages = kcalloc(nr_areas, sizeof(int), GFP_KERNEL);
23666a464dbSMichael Holzheu 	if (!rc->active_pages)
23766a464dbSMichael Holzheu 		goto fail_malloc_active_pages;
23866a464dbSMichael Holzheu 	if ((mode == ALL_AREAS) && (pages_per_area != 0)) {
23966a464dbSMichael Holzheu 		rc->areas = debug_areas_alloc(pages_per_area, nr_areas);
2401da177e4SLinus Torvalds 		if (!rc->areas)
2411da177e4SLinus Torvalds 			goto fail_malloc_areas;
2421da177e4SLinus Torvalds 	} else {
24366a464dbSMichael Holzheu 		rc->areas = NULL;
2441da177e4SLinus Torvalds 	}
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds 	/* initialize members */
2471da177e4SLinus Torvalds 	spin_lock_init(&rc->lock);
24866a464dbSMichael Holzheu 	rc->pages_per_area = pages_per_area;
2491da177e4SLinus Torvalds 	rc->nr_areas	   = nr_areas;
2501da177e4SLinus Torvalds 	rc->active_area    = 0;
25166a464dbSMichael Holzheu 	rc->level	   = level;
2521da177e4SLinus Torvalds 	rc->buf_size	   = buf_size;
2531da177e4SLinus Torvalds 	rc->entry_size	   = sizeof(debug_entry_t) + buf_size;
254820109fbSWolfram Sang 	strscpy(rc->name, name, sizeof(rc->name));
2551da177e4SLinus Torvalds 	memset(rc->views, 0, DEBUG_MAX_VIEWS * sizeof(struct debug_view *));
256496da0d7SHeiko Carstens 	memset(rc->debugfs_entries, 0, DEBUG_MAX_VIEWS * sizeof(struct dentry *));
257efc0c21cSElena Reshetova 	refcount_set(&(rc->ref_count), 0);
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	return rc;
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds fail_malloc_areas:
26266a464dbSMichael Holzheu 	kfree(rc->active_pages);
26366a464dbSMichael Holzheu fail_malloc_active_pages:
26466a464dbSMichael Holzheu 	kfree(rc->active_entries);
26566a464dbSMichael Holzheu fail_malloc_active_entries:
2661da177e4SLinus Torvalds 	kfree(rc);
2671da177e4SLinus Torvalds fail_malloc_rc:
2681da177e4SLinus Torvalds 	return NULL;
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds /*
27266a464dbSMichael Holzheu  * debug_areas_free
27366a464dbSMichael Holzheu  * - free all debug areas
27466a464dbSMichael Holzheu  */
debug_areas_free(debug_info_t * db_info)275496da0d7SHeiko Carstens static void debug_areas_free(debug_info_t *db_info)
27666a464dbSMichael Holzheu {
27766a464dbSMichael Holzheu 	int i, j;
27866a464dbSMichael Holzheu 
27966a464dbSMichael Holzheu 	if (!db_info->areas)
28066a464dbSMichael Holzheu 		return;
28166a464dbSMichael Holzheu 	for (i = 0; i < db_info->nr_areas; i++) {
282496da0d7SHeiko Carstens 		for (j = 0; j < db_info->pages_per_area; j++)
28366a464dbSMichael Holzheu 			kfree(db_info->areas[i][j]);
28466a464dbSMichael Holzheu 		kfree(db_info->areas[i]);
28566a464dbSMichael Holzheu 	}
28666a464dbSMichael Holzheu 	kfree(db_info->areas);
28766a464dbSMichael Holzheu 	db_info->areas = NULL;
28866a464dbSMichael Holzheu }
28966a464dbSMichael Holzheu 
29066a464dbSMichael Holzheu /*
2911da177e4SLinus Torvalds  * debug_info_free
2921da177e4SLinus Torvalds  * - free memory debug-info
2931da177e4SLinus Torvalds  */
debug_info_free(debug_info_t * db_info)294496da0d7SHeiko Carstens static void debug_info_free(debug_info_t *db_info)
295496da0d7SHeiko Carstens {
29666a464dbSMichael Holzheu 	debug_areas_free(db_info);
29766a464dbSMichael Holzheu 	kfree(db_info->active_entries);
29866a464dbSMichael Holzheu 	kfree(db_info->active_pages);
2991da177e4SLinus Torvalds 	kfree(db_info);
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds /*
3031da177e4SLinus Torvalds  * debug_info_create
3041da177e4SLinus Torvalds  * - create new debug-info
3051da177e4SLinus Torvalds  */
3061da177e4SLinus Torvalds 
debug_info_create(const char * name,int pages_per_area,int nr_areas,int buf_size,umode_t mode)307496da0d7SHeiko Carstens static debug_info_t *debug_info_create(const char *name, int pages_per_area,
308496da0d7SHeiko Carstens 				       int nr_areas, int buf_size, umode_t mode)
3091da177e4SLinus Torvalds {
3101da177e4SLinus Torvalds 	debug_info_t *rc;
3111da177e4SLinus Torvalds 
31266a464dbSMichael Holzheu 	rc = debug_info_alloc(name, pages_per_area, nr_areas, buf_size,
31366a464dbSMichael Holzheu 			      DEBUG_DEFAULT_LEVEL, ALL_AREAS);
3141da177e4SLinus Torvalds 	if (!rc)
3151da177e4SLinus Torvalds 		goto out;
3161da177e4SLinus Torvalds 
3179637c3f3SMichael Holzheu 	rc->mode = mode & ~S_IFMT;
318efc0c21cSElena Reshetova 	refcount_set(&rc->ref_count, 1);
3191da177e4SLinus Torvalds out:
3201da177e4SLinus Torvalds 	return rc;
3211da177e4SLinus Torvalds }
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds /*
3241da177e4SLinus Torvalds  * debug_info_copy
3251da177e4SLinus Torvalds  * - copy debug-info
3261da177e4SLinus Torvalds  */
debug_info_copy(debug_info_t * in,int mode)327496da0d7SHeiko Carstens static debug_info_t *debug_info_copy(debug_info_t *in, int mode)
3281da177e4SLinus Torvalds {
329942eaabdSMichael Holzheu 	unsigned long flags;
330496da0d7SHeiko Carstens 	debug_info_t *rc;
331496da0d7SHeiko Carstens 	int i, j;
33266a464dbSMichael Holzheu 
333942eaabdSMichael Holzheu 	/* get a consistent copy of the debug areas */
334942eaabdSMichael Holzheu 	do {
335942eaabdSMichael Holzheu 		rc = debug_info_alloc(in->name, in->pages_per_area,
336942eaabdSMichael Holzheu 			in->nr_areas, in->buf_size, in->level, mode);
337942eaabdSMichael Holzheu 		spin_lock_irqsave(&in->lock, flags);
338942eaabdSMichael Holzheu 		if (!rc)
339942eaabdSMichael Holzheu 			goto out;
340942eaabdSMichael Holzheu 		/* has something changed in the meantime ? */
341942eaabdSMichael Holzheu 		if ((rc->pages_per_area == in->pages_per_area) &&
342942eaabdSMichael Holzheu 		    (rc->nr_areas == in->nr_areas)) {
343942eaabdSMichael Holzheu 			break;
344942eaabdSMichael Holzheu 		}
345942eaabdSMichael Holzheu 		spin_unlock_irqrestore(&in->lock, flags);
346942eaabdSMichael Holzheu 		debug_info_free(rc);
347942eaabdSMichael Holzheu 	} while (1);
348942eaabdSMichael Holzheu 
349acfa922cSJulia Lawall 	if (mode == NO_AREAS)
3501da177e4SLinus Torvalds 		goto out;
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 	for (i = 0; i < in->nr_areas; i++) {
353496da0d7SHeiko Carstens 		for (j = 0; j < in->pages_per_area; j++)
35466a464dbSMichael Holzheu 			memcpy(rc->areas[i][j], in->areas[i][j], PAGE_SIZE);
355dc18c81aSNiklas Schnelle 		rc->active_pages[i] = in->active_pages[i];
356dc18c81aSNiklas Schnelle 		rc->active_entries[i] = in->active_entries[i];
35766a464dbSMichael Holzheu 	}
358dc18c81aSNiklas Schnelle 	rc->active_area = in->active_area;
3591da177e4SLinus Torvalds out:
360942eaabdSMichael Holzheu 	spin_unlock_irqrestore(&in->lock, flags);
3611da177e4SLinus Torvalds 	return rc;
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds /*
3651da177e4SLinus Torvalds  * debug_info_get
3661da177e4SLinus Torvalds  * - increments reference count for debug-info
3671da177e4SLinus Torvalds  */
debug_info_get(debug_info_t * db_info)368496da0d7SHeiko Carstens static void debug_info_get(debug_info_t *db_info)
3691da177e4SLinus Torvalds {
3701da177e4SLinus Torvalds 	if (db_info)
371efc0c21cSElena Reshetova 		refcount_inc(&db_info->ref_count);
3721da177e4SLinus Torvalds }
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds /*
3751da177e4SLinus Torvalds  * debug_info_put:
3761da177e4SLinus Torvalds  * - decreases reference count for debug-info and frees it if necessary
3771da177e4SLinus Torvalds  */
debug_info_put(debug_info_t * db_info)378496da0d7SHeiko Carstens static void debug_info_put(debug_info_t *db_info)
3791da177e4SLinus Torvalds {
3801da177e4SLinus Torvalds 	if (!db_info)
3811da177e4SLinus Torvalds 		return;
3829372a828SPeter Oberparleiter 	if (refcount_dec_and_test(&db_info->ref_count))
3831da177e4SLinus Torvalds 		debug_info_free(db_info);
3841da177e4SLinus Torvalds }
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds /*
3871da177e4SLinus Torvalds  * debug_format_entry:
388e6ebf0d6SHeiko Carstens  * - format one debug entry and return size of formatted data
3891da177e4SLinus Torvalds  */
debug_format_entry(file_private_info_t * p_info)390496da0d7SHeiko Carstens static int debug_format_entry(file_private_info_t *p_info)
3911da177e4SLinus Torvalds {
3921da177e4SLinus Torvalds 	debug_info_t *id_snap	= p_info->debug_info_snap;
3931da177e4SLinus Torvalds 	struct debug_view *view = p_info->view;
3941da177e4SLinus Torvalds 	debug_entry_t *act_entry;
3951da177e4SLinus Torvalds 	size_t len = 0;
396496da0d7SHeiko Carstens 
3971da177e4SLinus Torvalds 	if (p_info->act_entry == DEBUG_PROLOG_ENTRY) {
3981da177e4SLinus Torvalds 		/* print prolog */
399897614f9SNiklas Schnelle 		if (view->prolog_proc) {
400897614f9SNiklas Schnelle 			len += view->prolog_proc(id_snap, view, p_info->temp_buf,
401897614f9SNiklas Schnelle 						 sizeof(p_info->temp_buf));
402897614f9SNiklas Schnelle 		}
4031da177e4SLinus Torvalds 		goto out;
4041da177e4SLinus Torvalds 	}
40566a464dbSMichael Holzheu 	if (!id_snap->areas) /* this is true, if we have a prolog only view */
40666a464dbSMichael Holzheu 		goto out;    /* or if 'pages_per_area' is 0 */
40766a464dbSMichael Holzheu 	act_entry = (debug_entry_t *) ((char *)id_snap->areas[p_info->act_area]
40866a464dbSMichael Holzheu 				       [p_info->act_page] + p_info->act_entry);
4091da177e4SLinus Torvalds 
4100990d836SMikhail Zaslonko 	if (act_entry->clock == 0LL)
4111da177e4SLinus Torvalds 		goto out; /* empty entry */
412897614f9SNiklas Schnelle 	if (view->header_proc) {
41366a464dbSMichael Holzheu 		len += view->header_proc(id_snap, view, p_info->act_area,
414897614f9SNiklas Schnelle 					 act_entry, p_info->temp_buf + len,
415897614f9SNiklas Schnelle 					 sizeof(p_info->temp_buf) - len);
416897614f9SNiklas Schnelle 	}
417897614f9SNiklas Schnelle 	if (view->format_proc) {
41866a464dbSMichael Holzheu 		len += view->format_proc(id_snap, view, p_info->temp_buf + len,
419897614f9SNiklas Schnelle 					 sizeof(p_info->temp_buf) - len,
4201da177e4SLinus Torvalds 					 DEBUG_DATA(act_entry));
421897614f9SNiklas Schnelle 	}
4221da177e4SLinus Torvalds out:
4231da177e4SLinus Torvalds 	return len;
4241da177e4SLinus Torvalds }
4251da177e4SLinus Torvalds 
4267832b304SNiklas Schnelle /**
4277832b304SNiklas Schnelle  * debug_next_entry - Go to the next entry
4287832b304SNiklas Schnelle  * @p_info:	Private info that is manipulated
4297832b304SNiklas Schnelle  *
4307832b304SNiklas Schnelle  * Sets the current position in @p_info to the next entry. If no further entry
4317832b304SNiklas Schnelle  * exists the current position is set to one after the end the return value
4327832b304SNiklas Schnelle  * indicates that no further entries exist.
4337832b304SNiklas Schnelle  *
4347832b304SNiklas Schnelle  * Return: True if there are more following entries, false otherwise
4351da177e4SLinus Torvalds  */
debug_next_entry(file_private_info_t * p_info)4367832b304SNiklas Schnelle static inline bool debug_next_entry(file_private_info_t *p_info)
4371da177e4SLinus Torvalds {
43866a464dbSMichael Holzheu 	debug_info_t *id;
43966a464dbSMichael Holzheu 
44066a464dbSMichael Holzheu 	id = p_info->debug_info_snap;
4411da177e4SLinus Torvalds 	if (p_info->act_entry == DEBUG_PROLOG_ENTRY) {
4421da177e4SLinus Torvalds 		p_info->act_entry = 0;
44366a464dbSMichael Holzheu 		p_info->act_page  = 0;
4447832b304SNiklas Schnelle 		return true;
4451da177e4SLinus Torvalds 	}
44666a464dbSMichael Holzheu 	if (!id->areas)
4477832b304SNiklas Schnelle 		return false;
44866a464dbSMichael Holzheu 	p_info->act_entry += id->entry_size;
44966a464dbSMichael Holzheu 	/* switch to next page, if we reached the end of the page  */
45066a464dbSMichael Holzheu 	if (p_info->act_entry > (PAGE_SIZE - id->entry_size)) {
45166a464dbSMichael Holzheu 		/* next page */
4521da177e4SLinus Torvalds 		p_info->act_entry = 0;
45366a464dbSMichael Holzheu 		p_info->act_page += 1;
45466a464dbSMichael Holzheu 		if ((p_info->act_page % id->pages_per_area) == 0) {
45566a464dbSMichael Holzheu 			/* next area */
4561da177e4SLinus Torvalds 			p_info->act_area++;
45766a464dbSMichael Holzheu 			p_info->act_page = 0;
45866a464dbSMichael Holzheu 		}
4591da177e4SLinus Torvalds 		if (p_info->act_area >= id->nr_areas)
4607832b304SNiklas Schnelle 			return false;
4611da177e4SLinus Torvalds 	}
4627832b304SNiklas Schnelle 	return true;
4631da177e4SLinus Torvalds }
4641da177e4SLinus Torvalds 
465dc18c81aSNiklas Schnelle /**
466dc18c81aSNiklas Schnelle  * debug_to_act_entry - Go to the currently active entry
467dc18c81aSNiklas Schnelle  * @p_info:	Private info that is manipulated
468dc18c81aSNiklas Schnelle  *
469dc18c81aSNiklas Schnelle  * Sets the current position in @p_info to the currently active
470dc18c81aSNiklas Schnelle  * entry of @p_info->debug_info_snap
471dc18c81aSNiklas Schnelle  */
debug_to_act_entry(file_private_info_t * p_info)472dc18c81aSNiklas Schnelle static void debug_to_act_entry(file_private_info_t *p_info)
473dc18c81aSNiklas Schnelle {
474dc18c81aSNiklas Schnelle 	debug_info_t *snap_id;
475dc18c81aSNiklas Schnelle 
476dc18c81aSNiklas Schnelle 	snap_id = p_info->debug_info_snap;
477dc18c81aSNiklas Schnelle 	p_info->act_area = snap_id->active_area;
478dc18c81aSNiklas Schnelle 	p_info->act_page = snap_id->active_pages[snap_id->active_area];
479dc18c81aSNiklas Schnelle 	p_info->act_entry = snap_id->active_entries[snap_id->active_area];
480dc18c81aSNiklas Schnelle }
481dc18c81aSNiklas Schnelle 
482dc18c81aSNiklas Schnelle /**
483dc18c81aSNiklas Schnelle  * debug_prev_entry - Go to the previous entry
484dc18c81aSNiklas Schnelle  * @p_info:	Private info that is manipulated
485dc18c81aSNiklas Schnelle  *
486dc18c81aSNiklas Schnelle  * Sets the current position in @p_info to the previous entry. If no previous entry
487dc18c81aSNiklas Schnelle  * exists the current position is set left as DEBUG_PROLOG_ENTRY and the return value
488dc18c81aSNiklas Schnelle  * indicates that no previous entries exist.
489dc18c81aSNiklas Schnelle  *
490dc18c81aSNiklas Schnelle  * Return: True if there are more previous entries, false otherwise
491dc18c81aSNiklas Schnelle  */
492dc18c81aSNiklas Schnelle 
debug_prev_entry(file_private_info_t * p_info)493dc18c81aSNiklas Schnelle static inline bool debug_prev_entry(file_private_info_t *p_info)
494dc18c81aSNiklas Schnelle {
495dc18c81aSNiklas Schnelle 	debug_info_t *id;
496dc18c81aSNiklas Schnelle 
497dc18c81aSNiklas Schnelle 	id = p_info->debug_info_snap;
498dc18c81aSNiklas Schnelle 	if (p_info->act_entry == DEBUG_PROLOG_ENTRY)
499dc18c81aSNiklas Schnelle 		debug_to_act_entry(p_info);
500dc18c81aSNiklas Schnelle 	if (!id->areas)
501dc18c81aSNiklas Schnelle 		return false;
502dc18c81aSNiklas Schnelle 	p_info->act_entry -= id->entry_size;
503dc18c81aSNiklas Schnelle 	/* switch to prev page, if we reached the beginning of the page  */
504dc18c81aSNiklas Schnelle 	if (p_info->act_entry < 0) {
505dc18c81aSNiklas Schnelle 		/* end of previous page */
506dc18c81aSNiklas Schnelle 		p_info->act_entry = rounddown(PAGE_SIZE, id->entry_size) - id->entry_size;
507dc18c81aSNiklas Schnelle 		p_info->act_page--;
508dc18c81aSNiklas Schnelle 		if (p_info->act_page < 0) {
509dc18c81aSNiklas Schnelle 			/* previous area */
510dc18c81aSNiklas Schnelle 			p_info->act_area--;
511dc18c81aSNiklas Schnelle 			p_info->act_page = id->pages_per_area - 1;
512dc18c81aSNiklas Schnelle 		}
513dc18c81aSNiklas Schnelle 		if (p_info->act_area < 0)
514dc18c81aSNiklas Schnelle 			p_info->act_area = (id->nr_areas - 1) % id->nr_areas;
515dc18c81aSNiklas Schnelle 	}
516dc18c81aSNiklas Schnelle 	/* check full circle */
517dc18c81aSNiklas Schnelle 	if (id->active_area == p_info->act_area &&
518dc18c81aSNiklas Schnelle 	    id->active_pages[id->active_area] == p_info->act_page &&
519dc18c81aSNiklas Schnelle 	    id->active_entries[id->active_area] == p_info->act_entry)
520dc18c81aSNiklas Schnelle 		return false;
521dc18c81aSNiklas Schnelle 	return true;
522dc18c81aSNiklas Schnelle }
523dc18c81aSNiklas Schnelle 
524dc18c81aSNiklas Schnelle /**
525dc18c81aSNiklas Schnelle  * debug_move_entry - Go to next entry in either the forward or backward direction
526dc18c81aSNiklas Schnelle  * @p_info:	Private info that is manipulated
527dc18c81aSNiklas Schnelle  * @reverse:	If true go to the next entry in reverse i.e. previous
528dc18c81aSNiklas Schnelle  *
529dc18c81aSNiklas Schnelle  * Sets the current position in @p_info to the next (@reverse == false) or
530dc18c81aSNiklas Schnelle  * previous (@reverse == true) entry.
531dc18c81aSNiklas Schnelle  *
532dc18c81aSNiklas Schnelle  * Return: True if there are further entries in that direction,
533dc18c81aSNiklas Schnelle  * false otherwise.
534dc18c81aSNiklas Schnelle  */
debug_move_entry(file_private_info_t * p_info,bool reverse)535dc18c81aSNiklas Schnelle static bool debug_move_entry(file_private_info_t *p_info, bool reverse)
536dc18c81aSNiklas Schnelle {
537dc18c81aSNiklas Schnelle 	if (reverse)
538dc18c81aSNiklas Schnelle 		return debug_prev_entry(p_info);
539dc18c81aSNiklas Schnelle 	else
540dc18c81aSNiklas Schnelle 		return debug_next_entry(p_info);
541dc18c81aSNiklas Schnelle }
542dc18c81aSNiklas Schnelle 
5431da177e4SLinus Torvalds /*
5441da177e4SLinus Torvalds  * debug_output:
5451da177e4SLinus Torvalds  * - called for user read()
546e6ebf0d6SHeiko Carstens  * - copies formatted debug entries to the user buffer
5471da177e4SLinus Torvalds  */
debug_output(struct file * file,char __user * user_buf,size_t len,loff_t * offset)548496da0d7SHeiko Carstens static ssize_t debug_output(struct file *file,		/* file descriptor */
5491da177e4SLinus Torvalds 			    char __user *user_buf,	/* user buffer */
5501da177e4SLinus Torvalds 			    size_t len,			/* length of buffer */
5511da177e4SLinus Torvalds 			    loff_t *offset)		/* offset in the file */
5521da177e4SLinus Torvalds {
5531da177e4SLinus Torvalds 	size_t count = 0;
55466a464dbSMichael Holzheu 	size_t entry_offset;
5551da177e4SLinus Torvalds 	file_private_info_t *p_info;
5561da177e4SLinus Torvalds 
557496da0d7SHeiko Carstens 	p_info = (file_private_info_t *) file->private_data;
5581da177e4SLinus Torvalds 	if (*offset != p_info->offset)
5591da177e4SLinus Torvalds 		return -EPIPE;
5601da177e4SLinus Torvalds 	if (p_info->act_area >= p_info->debug_info_snap->nr_areas)
5611da177e4SLinus Torvalds 		return 0;
5621da177e4SLinus Torvalds 	entry_offset = p_info->act_entry_offset;
5631da177e4SLinus Torvalds 	while (count < len) {
56466a464dbSMichael Holzheu 		int formatted_line_residue;
565496da0d7SHeiko Carstens 		int formatted_line_size;
56666a464dbSMichael Holzheu 		int user_buf_residue;
56766a464dbSMichael Holzheu 		size_t copy_size;
5681da177e4SLinus Torvalds 
56966a464dbSMichael Holzheu 		formatted_line_size = debug_format_entry(p_info);
57066a464dbSMichael Holzheu 		formatted_line_residue = formatted_line_size - entry_offset;
57166a464dbSMichael Holzheu 		user_buf_residue = len-count;
57266a464dbSMichael Holzheu 		copy_size = min(user_buf_residue, formatted_line_residue);
57366a464dbSMichael Holzheu 		if (copy_size) {
57466a464dbSMichael Holzheu 			if (copy_to_user(user_buf + count, p_info->temp_buf
57566a464dbSMichael Holzheu 					 + entry_offset, copy_size))
5761da177e4SLinus Torvalds 				return -EFAULT;
57766a464dbSMichael Holzheu 			count += copy_size;
57866a464dbSMichael Holzheu 			entry_offset += copy_size;
5791da177e4SLinus Torvalds 		}
58066a464dbSMichael Holzheu 		if (copy_size == formatted_line_residue) {
5811da177e4SLinus Torvalds 			entry_offset = 0;
5827832b304SNiklas Schnelle 			if (!debug_next_entry(p_info))
5831da177e4SLinus Torvalds 				goto out;
5841da177e4SLinus Torvalds 		}
58566a464dbSMichael Holzheu 	}
5861da177e4SLinus Torvalds out:
5871da177e4SLinus Torvalds 	p_info->offset		 = *offset + count;
58866a464dbSMichael Holzheu 	p_info->act_entry_offset = entry_offset;
5891da177e4SLinus Torvalds 	*offset = p_info->offset;
5901da177e4SLinus Torvalds 	return count;
5911da177e4SLinus Torvalds }
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds /*
5941da177e4SLinus Torvalds  * debug_input:
5951da177e4SLinus Torvalds  * - called for user write()
5961da177e4SLinus Torvalds  * - calls input function of view
5971da177e4SLinus Torvalds  */
debug_input(struct file * file,const char __user * user_buf,size_t length,loff_t * offset)598496da0d7SHeiko Carstens static ssize_t debug_input(struct file *file, const char __user *user_buf,
599496da0d7SHeiko Carstens 			   size_t length, loff_t *offset)
6001da177e4SLinus Torvalds {
6011da177e4SLinus Torvalds 	file_private_info_t *p_info;
602496da0d7SHeiko Carstens 	int rc = 0;
6031da177e4SLinus Torvalds 
604e11f0d04SChristoph Hellwig 	mutex_lock(&debug_mutex);
6051da177e4SLinus Torvalds 	p_info = ((file_private_info_t *) file->private_data);
606496da0d7SHeiko Carstens 	if (p_info->view->input_proc) {
6071da177e4SLinus Torvalds 		rc = p_info->view->input_proc(p_info->debug_info_org,
6081da177e4SLinus Torvalds 					      p_info->view, file, user_buf,
6091da177e4SLinus Torvalds 					      length, offset);
610496da0d7SHeiko Carstens 	} else {
6111da177e4SLinus Torvalds 		rc = -EPERM;
612496da0d7SHeiko Carstens 	}
613e11f0d04SChristoph Hellwig 	mutex_unlock(&debug_mutex);
6141da177e4SLinus Torvalds 	return rc; /* number of input characters */
6151da177e4SLinus Torvalds }
6161da177e4SLinus Torvalds 
debug_file_private_alloc(debug_info_t * debug_info,struct debug_view * view)617460c52a5SNiklas Schnelle static file_private_info_t *debug_file_private_alloc(debug_info_t *debug_info,
618460c52a5SNiklas Schnelle 						     struct debug_view *view)
619460c52a5SNiklas Schnelle {
620460c52a5SNiklas Schnelle 	debug_info_t *debug_info_snapshot;
621460c52a5SNiklas Schnelle 	file_private_info_t *p_info;
622460c52a5SNiklas Schnelle 
623460c52a5SNiklas Schnelle 	/*
624460c52a5SNiklas Schnelle 	 * Make snapshot of current debug areas to get it consistent.
625460c52a5SNiklas Schnelle 	 * To copy all the areas is only needed, if we have a view which
626460c52a5SNiklas Schnelle 	 * formats the debug areas.
627460c52a5SNiklas Schnelle 	 */
628460c52a5SNiklas Schnelle 	if (!view->format_proc && !view->header_proc)
629460c52a5SNiklas Schnelle 		debug_info_snapshot = debug_info_copy(debug_info, NO_AREAS);
630460c52a5SNiklas Schnelle 	else
631460c52a5SNiklas Schnelle 		debug_info_snapshot = debug_info_copy(debug_info, ALL_AREAS);
632460c52a5SNiklas Schnelle 
633460c52a5SNiklas Schnelle 	if (!debug_info_snapshot)
634460c52a5SNiklas Schnelle 		return NULL;
635460c52a5SNiklas Schnelle 	p_info = kmalloc(sizeof(file_private_info_t), GFP_KERNEL);
636460c52a5SNiklas Schnelle 	if (!p_info) {
637460c52a5SNiklas Schnelle 		debug_info_free(debug_info_snapshot);
638460c52a5SNiklas Schnelle 		return NULL;
639460c52a5SNiklas Schnelle 	}
640460c52a5SNiklas Schnelle 	p_info->offset = 0;
641460c52a5SNiklas Schnelle 	p_info->debug_info_snap = debug_info_snapshot;
642460c52a5SNiklas Schnelle 	p_info->debug_info_org	= debug_info;
643460c52a5SNiklas Schnelle 	p_info->view = view;
644460c52a5SNiklas Schnelle 	p_info->act_area = 0;
645460c52a5SNiklas Schnelle 	p_info->act_page = 0;
646460c52a5SNiklas Schnelle 	p_info->act_entry = DEBUG_PROLOG_ENTRY;
647460c52a5SNiklas Schnelle 	p_info->act_entry_offset = 0;
648460c52a5SNiklas Schnelle 	debug_info_get(debug_info);
649460c52a5SNiklas Schnelle 
650460c52a5SNiklas Schnelle 	return p_info;
651460c52a5SNiklas Schnelle }
652460c52a5SNiklas Schnelle 
6531da177e4SLinus Torvalds /*
6541da177e4SLinus Torvalds  * debug_open:
6551da177e4SLinus Torvalds  * - called for user open()
656e6ebf0d6SHeiko Carstens  * - copies formatted output to private_data area of the file
6571da177e4SLinus Torvalds  *   handle
6581da177e4SLinus Torvalds  */
debug_open(struct inode * inode,struct file * file)659496da0d7SHeiko Carstens static int debug_open(struct inode *inode, struct file *file)
6601da177e4SLinus Torvalds {
661460c52a5SNiklas Schnelle 	debug_info_t *debug_info;
662496da0d7SHeiko Carstens 	file_private_info_t *p_info;
663496da0d7SHeiko Carstens 	int i, rc = 0;
6641da177e4SLinus Torvalds 
665e11f0d04SChristoph Hellwig 	mutex_lock(&debug_mutex);
666496ad9aaSAl Viro 	debug_info = file_inode(file)->i_private;
667942eaabdSMichael Holzheu 	/* find debug view */
6681da177e4SLinus Torvalds 	for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
66966a464dbSMichael Holzheu 		if (!debug_info->views[i])
6701da177e4SLinus Torvalds 			continue;
671496da0d7SHeiko Carstens 		else if (debug_info->debugfs_entries[i] == file->f_path.dentry)
6721da177e4SLinus Torvalds 			goto found; /* found view ! */
6731da177e4SLinus Torvalds 	}
6741da177e4SLinus Torvalds 	/* no entry found */
6751da177e4SLinus Torvalds 	rc = -EINVAL;
6761da177e4SLinus Torvalds 	goto out;
6771da177e4SLinus Torvalds 
6781da177e4SLinus Torvalds found:
679460c52a5SNiklas Schnelle 	p_info = debug_file_private_alloc(debug_info, debug_info->views[i]);
68066a464dbSMichael Holzheu 	if (!p_info) {
6811da177e4SLinus Torvalds 		rc = -ENOMEM;
6821da177e4SLinus Torvalds 		goto out;
6831da177e4SLinus Torvalds 	}
68466a464dbSMichael Holzheu 	file->private_data = p_info;
68558ea91c0SMartin Schwidefsky 	nonseekable_open(inode, file);
6861da177e4SLinus Torvalds out:
687e11f0d04SChristoph Hellwig 	mutex_unlock(&debug_mutex);
6881da177e4SLinus Torvalds 	return rc;
6891da177e4SLinus Torvalds }
6901da177e4SLinus Torvalds 
debug_file_private_free(file_private_info_t * p_info)691460c52a5SNiklas Schnelle static void debug_file_private_free(file_private_info_t *p_info)
692460c52a5SNiklas Schnelle {
693460c52a5SNiklas Schnelle 	if (p_info->debug_info_snap)
694460c52a5SNiklas Schnelle 		debug_info_free(p_info->debug_info_snap);
695460c52a5SNiklas Schnelle 	debug_info_put(p_info->debug_info_org);
696460c52a5SNiklas Schnelle 	kfree(p_info);
697460c52a5SNiklas Schnelle }
698460c52a5SNiklas Schnelle 
6991da177e4SLinus Torvalds /*
7001da177e4SLinus Torvalds  * debug_close:
7011da177e4SLinus Torvalds  * - called for user close()
7021da177e4SLinus Torvalds  * - deletes  private_data area of the file handle
7031da177e4SLinus Torvalds  */
debug_close(struct inode * inode,struct file * file)704496da0d7SHeiko Carstens static int debug_close(struct inode *inode, struct file *file)
7051da177e4SLinus Torvalds {
7061da177e4SLinus Torvalds 	file_private_info_t *p_info;
707496da0d7SHeiko Carstens 
7081da177e4SLinus Torvalds 	p_info = (file_private_info_t *) file->private_data;
709460c52a5SNiklas Schnelle 	debug_file_private_free(p_info);
710460c52a5SNiklas Schnelle 	file->private_data = NULL;
7111da177e4SLinus Torvalds 	return 0; /* success */
7121da177e4SLinus Torvalds }
7131da177e4SLinus Torvalds 
7145f952daeSNiklas Schnelle /**
7155f952daeSNiklas Schnelle  * debug_dump - Get a textual representation of debug info, or as much as fits
7165f952daeSNiklas Schnelle  * @id:		Debug information to use
7175f952daeSNiklas Schnelle  * @view:	View with which to dump the debug information
7185f952daeSNiklas Schnelle  * @buf:	Buffer the textual debug data representation is written to
7195f952daeSNiklas Schnelle  * @buf_size:	Size of the buffer, including the trailing '\0' byte
720dc18c81aSNiklas Schnelle  * @reverse:	Go backwards from the last written entry
7215f952daeSNiklas Schnelle  *
7225f952daeSNiklas Schnelle  * This function may be used whenever a textual representation of the debug
7235f952daeSNiklas Schnelle  * information is required without using an s390dbf file.
7245f952daeSNiklas Schnelle  *
7255f952daeSNiklas Schnelle  * Note: It is the callers responsibility to supply a view that is compatible
7265f952daeSNiklas Schnelle  * with the debug information data.
7275f952daeSNiklas Schnelle  *
7285f952daeSNiklas Schnelle  * Return: On success returns the number of bytes written to the buffer not
7295f952daeSNiklas Schnelle  * including the trailing '\0' byte. If bug_size == 0 the function returns 0.
7305f952daeSNiklas Schnelle  * On failure an error code less than 0 is returned.
7315f952daeSNiklas Schnelle  */
debug_dump(debug_info_t * id,struct debug_view * view,char * buf,size_t buf_size,bool reverse)7325f952daeSNiklas Schnelle ssize_t debug_dump(debug_info_t *id, struct debug_view *view,
733dc18c81aSNiklas Schnelle 		   char *buf, size_t buf_size, bool reverse)
7345f952daeSNiklas Schnelle {
7355f952daeSNiklas Schnelle 	file_private_info_t *p_info;
7365f952daeSNiklas Schnelle 	size_t size, offset = 0;
7375f952daeSNiklas Schnelle 
7385f952daeSNiklas Schnelle 	/* Need space for '\0' byte */
7395f952daeSNiklas Schnelle 	if (buf_size < 1)
7405f952daeSNiklas Schnelle 		return 0;
7415f952daeSNiklas Schnelle 	buf_size--;
7425f952daeSNiklas Schnelle 
7435f952daeSNiklas Schnelle 	p_info = debug_file_private_alloc(id, view);
7445f952daeSNiklas Schnelle 	if (!p_info)
7455f952daeSNiklas Schnelle 		return -ENOMEM;
7465f952daeSNiklas Schnelle 
7475f952daeSNiklas Schnelle 	/* There is always at least the DEBUG_PROLOG_ENTRY */
7485f952daeSNiklas Schnelle 	do {
7495f952daeSNiklas Schnelle 		size = debug_format_entry(p_info);
7505f952daeSNiklas Schnelle 		size = min(size, buf_size - offset);
7515f952daeSNiklas Schnelle 		memcpy(buf + offset, p_info->temp_buf, size);
7525f952daeSNiklas Schnelle 		offset += size;
7535f952daeSNiklas Schnelle 		if (offset >= buf_size)
7545f952daeSNiklas Schnelle 			break;
755dc18c81aSNiklas Schnelle 	} while (debug_move_entry(p_info, reverse));
7565f952daeSNiklas Schnelle 	debug_file_private_free(p_info);
7575f952daeSNiklas Schnelle 	buf[offset] = '\0';
7585f952daeSNiklas Schnelle 
7595f952daeSNiklas Schnelle 	return offset;
7605f952daeSNiklas Schnelle }
7615f952daeSNiklas Schnelle 
7629372a828SPeter Oberparleiter /* Create debugfs entries and add to internal list. */
_debug_register(debug_info_t * id)7639372a828SPeter Oberparleiter static void _debug_register(debug_info_t *id)
7649372a828SPeter Oberparleiter {
7659372a828SPeter Oberparleiter 	/* create root directory */
7669372a828SPeter Oberparleiter 	id->debugfs_root_entry = debugfs_create_dir(id->name,
7679372a828SPeter Oberparleiter 						    debug_debugfs_root_entry);
7689372a828SPeter Oberparleiter 
7699372a828SPeter Oberparleiter 	/* append new element to linked list */
7709372a828SPeter Oberparleiter 	if (!debug_area_first) {
7719372a828SPeter Oberparleiter 		/* first element in list */
7729372a828SPeter Oberparleiter 		debug_area_first = id;
7739372a828SPeter Oberparleiter 		id->prev = NULL;
7749372a828SPeter Oberparleiter 	} else {
7759372a828SPeter Oberparleiter 		/* append element to end of list */
7769372a828SPeter Oberparleiter 		debug_area_last->next = id;
7779372a828SPeter Oberparleiter 		id->prev = debug_area_last;
7789372a828SPeter Oberparleiter 	}
7799372a828SPeter Oberparleiter 	debug_area_last = id;
7809372a828SPeter Oberparleiter 	id->next = NULL;
7819372a828SPeter Oberparleiter 
7829372a828SPeter Oberparleiter 	debug_register_view(id, &debug_level_view);
7839372a828SPeter Oberparleiter 	debug_register_view(id, &debug_flush_view);
7849372a828SPeter Oberparleiter 	debug_register_view(id, &debug_pages_view);
7859372a828SPeter Oberparleiter }
7869372a828SPeter Oberparleiter 
7870328e519SSteffen Maier /**
7880328e519SSteffen Maier  * debug_register_mode() - creates and initializes debug area.
7890328e519SSteffen Maier  *
7900328e519SSteffen Maier  * @name:	Name of debug log (e.g. used for debugfs entry)
7910328e519SSteffen Maier  * @pages_per_area:	Number of pages, which will be allocated per area
7920328e519SSteffen Maier  * @nr_areas:	Number of debug areas
7930328e519SSteffen Maier  * @buf_size:	Size of data area in each debug entry
7940328e519SSteffen Maier  * @mode:	File mode for debugfs files. E.g. S_IRWXUGO
7950328e519SSteffen Maier  * @uid:	User ID for debugfs files. Currently only 0 is supported.
7960328e519SSteffen Maier  * @gid:	Group ID for debugfs files. Currently only 0 is supported.
7970328e519SSteffen Maier  *
7980328e519SSteffen Maier  * Return:
7990328e519SSteffen Maier  * - Handle for generated debug area
8000328e519SSteffen Maier  * - %NULL if register failed
8010328e519SSteffen Maier  *
8020328e519SSteffen Maier  * Allocates memory for a debug log.
8030328e519SSteffen Maier  * Must not be called within an interrupt handler.
8041da177e4SLinus Torvalds  */
debug_register_mode(const char * name,int pages_per_area,int nr_areas,int buf_size,umode_t mode,uid_t uid,gid_t gid)8055cbbf16aSCornelia Huck debug_info_t *debug_register_mode(const char *name, int pages_per_area,
806f4ae40a6SAl Viro 				  int nr_areas, int buf_size, umode_t mode,
8075cbbf16aSCornelia Huck 				  uid_t uid, gid_t gid)
8081da177e4SLinus Torvalds {
8091da177e4SLinus Torvalds 	debug_info_t *rc = NULL;
8101da177e4SLinus Torvalds 
8119637c3f3SMichael Holzheu 	/* Since debugfs currently does not support uid/gid other than root, */
8129637c3f3SMichael Holzheu 	/* we do not allow gid/uid != 0 until we get support for that. */
8139637c3f3SMichael Holzheu 	if ((uid != 0) || (gid != 0))
814baebc70aSJoe Perches 		pr_warn("Root becomes the owner of all s390dbf files in sysfs\n");
8156aa0d3a9SStoyan Gaydarov 	BUG_ON(!initialized);
8161da177e4SLinus Torvalds 
8171da177e4SLinus Torvalds 	/* create new debug_info */
8189637c3f3SMichael Holzheu 	rc = debug_info_create(name, pages_per_area, nr_areas, buf_size, mode);
8199372a828SPeter Oberparleiter 	if (rc) {
8209372a828SPeter Oberparleiter 		mutex_lock(&debug_mutex);
8219372a828SPeter Oberparleiter 		_debug_register(rc);
822e11f0d04SChristoph Hellwig 		mutex_unlock(&debug_mutex);
8239372a828SPeter Oberparleiter 	} else {
8249372a828SPeter Oberparleiter 		pr_err("Registering debug feature %s failed\n", name);
8259372a828SPeter Oberparleiter 	}
8261da177e4SLinus Torvalds 	return rc;
8271da177e4SLinus Torvalds }
8289637c3f3SMichael Holzheu EXPORT_SYMBOL(debug_register_mode);
8299637c3f3SMichael Holzheu 
8300328e519SSteffen Maier /**
8310328e519SSteffen Maier  * debug_register() - creates and initializes debug area with default file mode.
8320328e519SSteffen Maier  *
8330328e519SSteffen Maier  * @name:	Name of debug log (e.g. used for debugfs entry)
8340328e519SSteffen Maier  * @pages_per_area:	Number of pages, which will be allocated per area
8350328e519SSteffen Maier  * @nr_areas:	Number of debug areas
8360328e519SSteffen Maier  * @buf_size:	Size of data area in each debug entry
8370328e519SSteffen Maier  *
8380328e519SSteffen Maier  * Return:
8390328e519SSteffen Maier  * - Handle for generated debug area
8400328e519SSteffen Maier  * - %NULL if register failed
8410328e519SSteffen Maier  *
8420328e519SSteffen Maier  * Allocates memory for a debug log.
8430328e519SSteffen Maier  * The debugfs file mode access permissions are read and write for user.
8440328e519SSteffen Maier  * Must not be called within an interrupt handler.
8459637c3f3SMichael Holzheu  */
debug_register(const char * name,int pages_per_area,int nr_areas,int buf_size)8465cbbf16aSCornelia Huck debug_info_t *debug_register(const char *name, int pages_per_area,
8475cbbf16aSCornelia Huck 			     int nr_areas, int buf_size)
8489637c3f3SMichael Holzheu {
8499637c3f3SMichael Holzheu 	return debug_register_mode(name, pages_per_area, nr_areas, buf_size,
8509637c3f3SMichael Holzheu 				   S_IRUSR | S_IWUSR, 0, 0);
8519637c3f3SMichael Holzheu }
852d5274369SHeiko Carstens EXPORT_SYMBOL(debug_register);
8531da177e4SLinus Torvalds 
854d72541f9SPeter Oberparleiter /**
855d72541f9SPeter Oberparleiter  * debug_register_static() - registers a static debug area
856d72541f9SPeter Oberparleiter  *
857d72541f9SPeter Oberparleiter  * @id: Handle for static debug area
858d72541f9SPeter Oberparleiter  * @pages_per_area: Number of pages per area
859d72541f9SPeter Oberparleiter  * @nr_areas: Number of debug areas
860d72541f9SPeter Oberparleiter  *
861d72541f9SPeter Oberparleiter  * Register debug_info_t defined using DEFINE_STATIC_DEBUG_INFO.
862d72541f9SPeter Oberparleiter  *
863d72541f9SPeter Oberparleiter  * Note: This function is called automatically via an initcall generated by
864d72541f9SPeter Oberparleiter  *	 DEFINE_STATIC_DEBUG_INFO.
865d72541f9SPeter Oberparleiter  */
debug_register_static(debug_info_t * id,int pages_per_area,int nr_areas)866d72541f9SPeter Oberparleiter void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
867d72541f9SPeter Oberparleiter {
868d72541f9SPeter Oberparleiter 	unsigned long flags;
869d72541f9SPeter Oberparleiter 	debug_info_t *copy;
870d72541f9SPeter Oberparleiter 
871d72541f9SPeter Oberparleiter 	if (!initialized) {
872d72541f9SPeter Oberparleiter 		pr_err("Tried to register debug feature %s too early\n",
873d72541f9SPeter Oberparleiter 		       id->name);
874d72541f9SPeter Oberparleiter 		return;
875d72541f9SPeter Oberparleiter 	}
876d72541f9SPeter Oberparleiter 
877d72541f9SPeter Oberparleiter 	copy = debug_info_alloc("", pages_per_area, nr_areas, id->buf_size,
878d72541f9SPeter Oberparleiter 				id->level, ALL_AREAS);
879d72541f9SPeter Oberparleiter 	if (!copy) {
880d72541f9SPeter Oberparleiter 		pr_err("Registering debug feature %s failed\n", id->name);
881d72541f9SPeter Oberparleiter 
882d72541f9SPeter Oberparleiter 		/* Clear pointers to prevent tracing into released initdata. */
883d72541f9SPeter Oberparleiter 		spin_lock_irqsave(&id->lock, flags);
884d72541f9SPeter Oberparleiter 		id->areas = NULL;
885d72541f9SPeter Oberparleiter 		id->active_pages = NULL;
886d72541f9SPeter Oberparleiter 		id->active_entries = NULL;
887d72541f9SPeter Oberparleiter 		spin_unlock_irqrestore(&id->lock, flags);
888d72541f9SPeter Oberparleiter 
889d72541f9SPeter Oberparleiter 		return;
890d72541f9SPeter Oberparleiter 	}
891d72541f9SPeter Oberparleiter 
892d72541f9SPeter Oberparleiter 	/* Replace static trace area with dynamic copy. */
893d72541f9SPeter Oberparleiter 	spin_lock_irqsave(&id->lock, flags);
894d72541f9SPeter Oberparleiter 	debug_events_append(copy, id);
895d72541f9SPeter Oberparleiter 	debug_areas_swap(id, copy);
896d72541f9SPeter Oberparleiter 	spin_unlock_irqrestore(&id->lock, flags);
897d72541f9SPeter Oberparleiter 
898d72541f9SPeter Oberparleiter 	/* Clear pointers to initdata and discard copy. */
899d72541f9SPeter Oberparleiter 	copy->areas = NULL;
900d72541f9SPeter Oberparleiter 	copy->active_pages = NULL;
901d72541f9SPeter Oberparleiter 	copy->active_entries = NULL;
902d72541f9SPeter Oberparleiter 	debug_info_free(copy);
903d72541f9SPeter Oberparleiter 
904d72541f9SPeter Oberparleiter 	mutex_lock(&debug_mutex);
905d72541f9SPeter Oberparleiter 	_debug_register(id);
906d72541f9SPeter Oberparleiter 	mutex_unlock(&debug_mutex);
907d72541f9SPeter Oberparleiter }
908d72541f9SPeter Oberparleiter 
9099372a828SPeter Oberparleiter /* Remove debugfs entries and remove from internal list. */
_debug_unregister(debug_info_t * id)9109372a828SPeter Oberparleiter static void _debug_unregister(debug_info_t *id)
9119372a828SPeter Oberparleiter {
9129372a828SPeter Oberparleiter 	int i;
9139372a828SPeter Oberparleiter 
9149372a828SPeter Oberparleiter 	for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
9159372a828SPeter Oberparleiter 		if (!id->views[i])
9169372a828SPeter Oberparleiter 			continue;
9179372a828SPeter Oberparleiter 		debugfs_remove(id->debugfs_entries[i]);
9189372a828SPeter Oberparleiter 	}
9199372a828SPeter Oberparleiter 	debugfs_remove(id->debugfs_root_entry);
9209372a828SPeter Oberparleiter 	if (id == debug_area_first)
9219372a828SPeter Oberparleiter 		debug_area_first = id->next;
9229372a828SPeter Oberparleiter 	if (id == debug_area_last)
9239372a828SPeter Oberparleiter 		debug_area_last = id->prev;
9249372a828SPeter Oberparleiter 	if (id->prev)
9259372a828SPeter Oberparleiter 		id->prev->next = id->next;
9269372a828SPeter Oberparleiter 	if (id->next)
9279372a828SPeter Oberparleiter 		id->next->prev = id->prev;
9289372a828SPeter Oberparleiter }
9299372a828SPeter Oberparleiter 
9300328e519SSteffen Maier /**
9310328e519SSteffen Maier  * debug_unregister() - give back debug area.
9320328e519SSteffen Maier  *
9330328e519SSteffen Maier  * @id:		handle for debug log
9340328e519SSteffen Maier  *
9350328e519SSteffen Maier  * Return:
9360328e519SSteffen Maier  *    none
9371da177e4SLinus Torvalds  */
debug_unregister(debug_info_t * id)938496da0d7SHeiko Carstens void debug_unregister(debug_info_t *id)
9391da177e4SLinus Torvalds {
9401da177e4SLinus Torvalds 	if (!id)
941496da0d7SHeiko Carstens 		return;
942e11f0d04SChristoph Hellwig 	mutex_lock(&debug_mutex);
9439372a828SPeter Oberparleiter 	_debug_unregister(id);
944e11f0d04SChristoph Hellwig 	mutex_unlock(&debug_mutex);
9459372a828SPeter Oberparleiter 
9469372a828SPeter Oberparleiter 	debug_info_put(id);
9471da177e4SLinus Torvalds }
948d5274369SHeiko Carstens EXPORT_SYMBOL(debug_unregister);
9491da177e4SLinus Torvalds 
9501da177e4SLinus Torvalds /*
95166a464dbSMichael Holzheu  * debug_set_size:
95266a464dbSMichael Holzheu  * - set area size (number of pages) and number of areas
95366a464dbSMichael Holzheu  */
debug_set_size(debug_info_t * id,int nr_areas,int pages_per_area)954496da0d7SHeiko Carstens static int debug_set_size(debug_info_t *id, int nr_areas, int pages_per_area)
95566a464dbSMichael Holzheu {
95612047778SPeter Oberparleiter 	debug_info_t *new_id;
957496da0d7SHeiko Carstens 	unsigned long flags;
95866a464dbSMichael Holzheu 
95966a464dbSMichael Holzheu 	if (!id || (nr_areas <= 0) || (pages_per_area < 0))
96066a464dbSMichael Holzheu 		return -EINVAL;
96112047778SPeter Oberparleiter 
96212047778SPeter Oberparleiter 	new_id = debug_info_alloc("", pages_per_area, nr_areas, id->buf_size,
96312047778SPeter Oberparleiter 				  id->level, ALL_AREAS);
96412047778SPeter Oberparleiter 	if (!new_id) {
965c5612c19SMichael Holzheu 		pr_info("Allocating memory for %i pages failed\n",
966c5612c19SMichael Holzheu 			pages_per_area);
96712047778SPeter Oberparleiter 		return -ENOMEM;
96866a464dbSMichael Holzheu 	}
96912047778SPeter Oberparleiter 
97066a464dbSMichael Holzheu 	spin_lock_irqsave(&id->lock, flags);
97112047778SPeter Oberparleiter 	debug_events_append(new_id, id);
97212047778SPeter Oberparleiter 	debug_areas_swap(new_id, id);
97312047778SPeter Oberparleiter 	debug_info_free(new_id);
97466a464dbSMichael Holzheu 	spin_unlock_irqrestore(&id->lock, flags);
975c5612c19SMichael Holzheu 	pr_info("%s: set new size (%i pages)\n", id->name, pages_per_area);
97612047778SPeter Oberparleiter 
97712047778SPeter Oberparleiter 	return 0;
97866a464dbSMichael Holzheu }
97966a464dbSMichael Holzheu 
9800328e519SSteffen Maier /**
9810328e519SSteffen Maier  * debug_set_level() - Sets new actual debug level if new_level is valid.
9820328e519SSteffen Maier  *
9830328e519SSteffen Maier  * @id:		handle for debug log
9840328e519SSteffen Maier  * @new_level:	new debug level
9850328e519SSteffen Maier  *
9860328e519SSteffen Maier  * Return:
9870328e519SSteffen Maier  *    none
9881da177e4SLinus Torvalds  */
debug_set_level(debug_info_t * id,int new_level)989496da0d7SHeiko Carstens void debug_set_level(debug_info_t *id, int new_level)
9901da177e4SLinus Torvalds {
9911da177e4SLinus Torvalds 	unsigned long flags;
992496da0d7SHeiko Carstens 
9931da177e4SLinus Torvalds 	if (!id)
9941da177e4SLinus Torvalds 		return;
99570aa5d39SPeter Oberparleiter 
9961da177e4SLinus Torvalds 	if (new_level == DEBUG_OFF_LEVEL) {
997c5612c19SMichael Holzheu 		pr_info("%s: switched off\n", id->name);
9981da177e4SLinus Torvalds 	} else if ((new_level > DEBUG_MAX_LEVEL) || (new_level < 0)) {
999c5612c19SMichael Holzheu 		pr_info("%s: level %i is out of range (%i - %i)\n",
10001da177e4SLinus Torvalds 			id->name, new_level, 0, DEBUG_MAX_LEVEL);
100170aa5d39SPeter Oberparleiter 		return;
10021da177e4SLinus Torvalds 	}
100370aa5d39SPeter Oberparleiter 
100470aa5d39SPeter Oberparleiter 	spin_lock_irqsave(&id->lock, flags);
100570aa5d39SPeter Oberparleiter 	id->level = new_level;
10061da177e4SLinus Torvalds 	spin_unlock_irqrestore(&id->lock, flags);
10071da177e4SLinus Torvalds }
1008d5274369SHeiko Carstens EXPORT_SYMBOL(debug_set_level);
10091da177e4SLinus Torvalds 
10101da177e4SLinus Torvalds /*
10111da177e4SLinus Torvalds  * proceed_active_entry:
10121da177e4SLinus Torvalds  * - set active entry to next in the ring buffer
10131da177e4SLinus Torvalds  */
proceed_active_entry(debug_info_t * id)1014496da0d7SHeiko Carstens static inline void proceed_active_entry(debug_info_t *id)
10151da177e4SLinus Torvalds {
101666a464dbSMichael Holzheu 	if ((id->active_entries[id->active_area] += id->entry_size)
101766a464dbSMichael Holzheu 	    > (PAGE_SIZE - id->entry_size)) {
101866a464dbSMichael Holzheu 		id->active_entries[id->active_area] = 0;
101966a464dbSMichael Holzheu 		id->active_pages[id->active_area] =
102066a464dbSMichael Holzheu 			(id->active_pages[id->active_area] + 1) %
102166a464dbSMichael Holzheu 			id->pages_per_area;
102266a464dbSMichael Holzheu 	}
10231da177e4SLinus Torvalds }
10241da177e4SLinus Torvalds 
10251da177e4SLinus Torvalds /*
10261da177e4SLinus Torvalds  * proceed_active_area:
10271da177e4SLinus Torvalds  * - set active area to next in the ring buffer
10281da177e4SLinus Torvalds  */
proceed_active_area(debug_info_t * id)1029496da0d7SHeiko Carstens static inline void proceed_active_area(debug_info_t *id)
10301da177e4SLinus Torvalds {
10311da177e4SLinus Torvalds 	id->active_area++;
10321da177e4SLinus Torvalds 	id->active_area = id->active_area % id->nr_areas;
10331da177e4SLinus Torvalds }
10341da177e4SLinus Torvalds 
10351da177e4SLinus Torvalds /*
10361da177e4SLinus Torvalds  * get_active_entry:
10371da177e4SLinus Torvalds  */
get_active_entry(debug_info_t * id)1038496da0d7SHeiko Carstens static inline debug_entry_t *get_active_entry(debug_info_t *id)
10391da177e4SLinus Torvalds {
104066a464dbSMichael Holzheu 	return (debug_entry_t *) (((char *) id->areas[id->active_area]
104166a464dbSMichael Holzheu 				   [id->active_pages[id->active_area]]) +
104266a464dbSMichael Holzheu 				  id->active_entries[id->active_area]);
10431da177e4SLinus Torvalds }
10441da177e4SLinus Torvalds 
104512047778SPeter Oberparleiter /* Swap debug areas of a and b. */
debug_areas_swap(debug_info_t * a,debug_info_t * b)104612047778SPeter Oberparleiter static void debug_areas_swap(debug_info_t *a, debug_info_t *b)
104712047778SPeter Oberparleiter {
104812047778SPeter Oberparleiter 	swap(a->nr_areas, b->nr_areas);
104912047778SPeter Oberparleiter 	swap(a->pages_per_area, b->pages_per_area);
105012047778SPeter Oberparleiter 	swap(a->areas, b->areas);
105112047778SPeter Oberparleiter 	swap(a->active_area, b->active_area);
105212047778SPeter Oberparleiter 	swap(a->active_pages, b->active_pages);
105312047778SPeter Oberparleiter 	swap(a->active_entries, b->active_entries);
105412047778SPeter Oberparleiter }
105512047778SPeter Oberparleiter 
105612047778SPeter Oberparleiter /* Append all debug events in active area from source to destination log. */
debug_events_append(debug_info_t * dest,debug_info_t * src)105712047778SPeter Oberparleiter static void debug_events_append(debug_info_t *dest, debug_info_t *src)
105812047778SPeter Oberparleiter {
105912047778SPeter Oberparleiter 	debug_entry_t *from, *to, *last;
106012047778SPeter Oberparleiter 
106112047778SPeter Oberparleiter 	if (!src->areas || !dest->areas)
106212047778SPeter Oberparleiter 		return;
106312047778SPeter Oberparleiter 
106412047778SPeter Oberparleiter 	/* Loop over all entries in src, starting with oldest. */
106512047778SPeter Oberparleiter 	from = get_active_entry(src);
106612047778SPeter Oberparleiter 	last = from;
106712047778SPeter Oberparleiter 	do {
106812047778SPeter Oberparleiter 		if (from->clock != 0LL) {
106912047778SPeter Oberparleiter 			to = get_active_entry(dest);
107012047778SPeter Oberparleiter 			memset(to, 0, dest->entry_size);
107112047778SPeter Oberparleiter 			memcpy(to, from, min(src->entry_size,
107212047778SPeter Oberparleiter 					     dest->entry_size));
107312047778SPeter Oberparleiter 			proceed_active_entry(dest);
107412047778SPeter Oberparleiter 		}
107512047778SPeter Oberparleiter 
107612047778SPeter Oberparleiter 		proceed_active_entry(src);
107712047778SPeter Oberparleiter 		from = get_active_entry(src);
107812047778SPeter Oberparleiter 	} while (from != last);
107912047778SPeter Oberparleiter }
108012047778SPeter Oberparleiter 
10811da177e4SLinus Torvalds /*
10821da177e4SLinus Torvalds  * debug_finish_entry:
10831da177e4SLinus Torvalds  * - set timestamp, caller address, cpu number etc.
10841da177e4SLinus Torvalds  */
10851da177e4SLinus Torvalds 
debug_finish_entry(debug_info_t * id,debug_entry_t * active,int level,int exception)1086496da0d7SHeiko Carstens static inline void debug_finish_entry(debug_info_t *id, debug_entry_t *active,
1087496da0d7SHeiko Carstens 				      int level, int exception)
10881da177e4SLinus Torvalds {
10890990d836SMikhail Zaslonko 	unsigned long timestamp;
1090d1deda6fSHeiko Carstens 	union tod_clock clk;
10910990d836SMikhail Zaslonko 
1092d1deda6fSHeiko Carstens 	store_tod_clock_ext(&clk);
1093d1deda6fSHeiko Carstens 	timestamp = clk.us;
10940990d836SMikhail Zaslonko 	timestamp -= TOD_UNIX_EPOCH >> 12;
10950990d836SMikhail Zaslonko 	active->clock = timestamp;
10960990d836SMikhail Zaslonko 	active->cpu = smp_processor_id();
10971da177e4SLinus Torvalds 	active->caller = __builtin_return_address(0);
10980990d836SMikhail Zaslonko 	active->exception = exception;
10990990d836SMikhail Zaslonko 	active->level = level;
11001da177e4SLinus Torvalds 	proceed_active_entry(id);
11011da177e4SLinus Torvalds 	if (exception)
11021da177e4SLinus Torvalds 		proceed_active_area(id);
11031da177e4SLinus Torvalds }
11041da177e4SLinus Torvalds 
11051da177e4SLinus Torvalds static int debug_stoppable = 1;
11061da177e4SLinus Torvalds static int debug_active = 1;
11071da177e4SLinus Torvalds 
11081da177e4SLinus Torvalds #define CTL_S390DBF_STOPPABLE 5678
11091da177e4SLinus Torvalds #define CTL_S390DBF_ACTIVE 5679
11101da177e4SLinus Torvalds 
11111da177e4SLinus Torvalds /*
11121da177e4SLinus Torvalds  * proc handler for the running debug_active sysctl
11131da177e4SLinus Torvalds  * always allow read, allow write only if debug_stoppable is set or
11141da177e4SLinus Torvalds  * if debug_active is already off
11151da177e4SLinus Torvalds  */
s390dbf_procactive(const struct ctl_table * table,int write,void * buffer,size_t * lenp,loff_t * ppos)111678eb4ea2SJoel Granados static int s390dbf_procactive(const struct ctl_table *table, int write,
111732927393SChristoph Hellwig 			      void *buffer, size_t *lenp, loff_t *ppos)
11181da177e4SLinus Torvalds {
11191da177e4SLinus Torvalds 	if (!write || debug_stoppable || !debug_active)
11208d65af78SAlexey Dobriyan 		return proc_dointvec(table, write, buffer, lenp, ppos);
11211da177e4SLinus Torvalds 	else
11221da177e4SLinus Torvalds 		return 0;
11231da177e4SLinus Torvalds }
11241da177e4SLinus Torvalds 
1125*1751f872SJoel Granados static const struct ctl_table s390dbf_table[] = {
11261da177e4SLinus Torvalds 	{
11271da177e4SLinus Torvalds 		.procname	= "debug_stoppable",
11281da177e4SLinus Torvalds 		.data		= &debug_stoppable,
11291da177e4SLinus Torvalds 		.maxlen		= sizeof(int),
11301da177e4SLinus Torvalds 		.mode		= S_IRUGO | S_IWUSR,
11316d456111SEric W. Biederman 		.proc_handler	= proc_dointvec,
11321da177e4SLinus Torvalds 	},
11331da177e4SLinus Torvalds 	{
11341da177e4SLinus Torvalds 		.procname	= "debug_active",
11351da177e4SLinus Torvalds 		.data		= &debug_active,
11361da177e4SLinus Torvalds 		.maxlen		= sizeof(int),
11371da177e4SLinus Torvalds 		.mode		= S_IRUGO | S_IWUSR,
11386d456111SEric W. Biederman 		.proc_handler	= s390dbf_procactive,
11391da177e4SLinus Torvalds 	},
11401da177e4SLinus Torvalds };
11411da177e4SLinus Torvalds 
11422b67fc46SHeiko Carstens static struct ctl_table_header *s390dbf_sysctl_header;
11431da177e4SLinus Torvalds 
11440328e519SSteffen Maier /**
11450328e519SSteffen Maier  * debug_stop_all() - stops the debug feature if stopping is allowed.
11460328e519SSteffen Maier  *
11470328e519SSteffen Maier  * Return:
11480328e519SSteffen Maier  * -   none
11490328e519SSteffen Maier  *
11500328e519SSteffen Maier  * Currently used in case of a kernel oops.
11510328e519SSteffen Maier  */
debug_stop_all(void)1152496da0d7SHeiko Carstens void debug_stop_all(void)
11531da177e4SLinus Torvalds {
11541da177e4SLinus Torvalds 	if (debug_stoppable)
11551da177e4SLinus Torvalds 		debug_active = 0;
11561da177e4SLinus Torvalds }
1157d5274369SHeiko Carstens EXPORT_SYMBOL(debug_stop_all);
11581da177e4SLinus Torvalds 
11590328e519SSteffen Maier /**
11600328e519SSteffen Maier  * debug_set_critical() - event/exception functions try lock instead of spin.
11610328e519SSteffen Maier  *
11620328e519SSteffen Maier  * Return:
11630328e519SSteffen Maier  * -   none
11640328e519SSteffen Maier  *
11650328e519SSteffen Maier  * Currently used in case of stopping all CPUs but the current one.
11660328e519SSteffen Maier  * Once in this state, functions to write a debug entry for an
11670328e519SSteffen Maier  * event or exception no longer spin on the debug area lock,
11680328e519SSteffen Maier  * but only try to get it and fail if they do not get the lock.
11690328e519SSteffen Maier  */
debug_set_critical(void)11703ab121abSMichael Holzheu void debug_set_critical(void)
11713ab121abSMichael Holzheu {
11723ab121abSMichael Holzheu 	debug_critical = 1;
11733ab121abSMichael Holzheu }
11743ab121abSMichael Holzheu 
11751da177e4SLinus Torvalds /*
11761da177e4SLinus Torvalds  * debug_event_common:
11771da177e4SLinus Torvalds  * - write debug entry with given size
11781da177e4SLinus Torvalds  */
debug_event_common(debug_info_t * id,int level,const void * buf,int len)1179496da0d7SHeiko Carstens debug_entry_t *debug_event_common(debug_info_t *id, int level, const void *buf,
1180496da0d7SHeiko Carstens 				  int len)
11811da177e4SLinus Torvalds {
11821da177e4SLinus Torvalds 	debug_entry_t *active;
1183496da0d7SHeiko Carstens 	unsigned long flags;
11841da177e4SLinus Torvalds 
118566a464dbSMichael Holzheu 	if (!debug_active || !id->areas)
11861da177e4SLinus Torvalds 		return NULL;
11873ab121abSMichael Holzheu 	if (debug_critical) {
11883ab121abSMichael Holzheu 		if (!spin_trylock_irqsave(&id->lock, flags))
11893ab121abSMichael Holzheu 			return NULL;
1190496da0d7SHeiko Carstens 	} else {
11911da177e4SLinus Torvalds 		spin_lock_irqsave(&id->lock, flags);
1192496da0d7SHeiko Carstens 	}
119394158e54SSebastian Ott 	do {
11941da177e4SLinus Torvalds 		active = get_active_entry(id);
11951da177e4SLinus Torvalds 		memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size));
11960dcd91a9SSebastian Ott 		if (len < id->buf_size)
11970dcd91a9SSebastian Ott 			memset((DEBUG_DATA(active)) + len, 0, id->buf_size - len);
11981da177e4SLinus Torvalds 		debug_finish_entry(id, active, level, 0);
119994158e54SSebastian Ott 		len -= id->buf_size;
120094158e54SSebastian Ott 		buf += id->buf_size;
120194158e54SSebastian Ott 	} while (len > 0);
12021da177e4SLinus Torvalds 
120394158e54SSebastian Ott 	spin_unlock_irqrestore(&id->lock, flags);
12041da177e4SLinus Torvalds 	return active;
12051da177e4SLinus Torvalds }
1206d5274369SHeiko Carstens EXPORT_SYMBOL(debug_event_common);
12071da177e4SLinus Torvalds 
12081da177e4SLinus Torvalds /*
12091da177e4SLinus Torvalds  * debug_exception_common:
12101da177e4SLinus Torvalds  * - write debug entry with given size and switch to next debug area
12111da177e4SLinus Torvalds  */
debug_exception_common(debug_info_t * id,int level,const void * buf,int len)1212496da0d7SHeiko Carstens debug_entry_t *debug_exception_common(debug_info_t *id, int level,
1213496da0d7SHeiko Carstens 				      const void *buf, int len)
12141da177e4SLinus Torvalds {
12151da177e4SLinus Torvalds 	debug_entry_t *active;
1216496da0d7SHeiko Carstens 	unsigned long flags;
12171da177e4SLinus Torvalds 
121866a464dbSMichael Holzheu 	if (!debug_active || !id->areas)
12191da177e4SLinus Torvalds 		return NULL;
12203ab121abSMichael Holzheu 	if (debug_critical) {
12213ab121abSMichael Holzheu 		if (!spin_trylock_irqsave(&id->lock, flags))
12223ab121abSMichael Holzheu 			return NULL;
1223496da0d7SHeiko Carstens 	} else {
12241da177e4SLinus Torvalds 		spin_lock_irqsave(&id->lock, flags);
1225496da0d7SHeiko Carstens 	}
122694158e54SSebastian Ott 	do {
12271da177e4SLinus Torvalds 		active = get_active_entry(id);
12281da177e4SLinus Torvalds 		memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size));
12290dcd91a9SSebastian Ott 		if (len < id->buf_size)
12300dcd91a9SSebastian Ott 			memset((DEBUG_DATA(active)) + len, 0, id->buf_size - len);
123194158e54SSebastian Ott 		debug_finish_entry(id, active, level, len <= id->buf_size);
123294158e54SSebastian Ott 		len -= id->buf_size;
123394158e54SSebastian Ott 		buf += id->buf_size;
123494158e54SSebastian Ott 	} while (len > 0);
12351da177e4SLinus Torvalds 
123694158e54SSebastian Ott 	spin_unlock_irqrestore(&id->lock, flags);
12371da177e4SLinus Torvalds 	return active;
12381da177e4SLinus Torvalds }
1239d5274369SHeiko Carstens EXPORT_SYMBOL(debug_exception_common);
12401da177e4SLinus Torvalds 
12411da177e4SLinus Torvalds /*
12421da177e4SLinus Torvalds  * counts arguments in format string for sprintf view
12431da177e4SLinus Torvalds  */
debug_count_numargs(char * string)1244496da0d7SHeiko Carstens static inline int debug_count_numargs(char *string)
12451da177e4SLinus Torvalds {
12461da177e4SLinus Torvalds 	int numargs = 0;
12471da177e4SLinus Torvalds 
12481da177e4SLinus Torvalds 	while (*string) {
12491da177e4SLinus Torvalds 		if (*string++ == '%')
12501da177e4SLinus Torvalds 			numargs++;
12511da177e4SLinus Torvalds 	}
1252496da0d7SHeiko Carstens 	return numargs;
12531da177e4SLinus Torvalds }
12541da177e4SLinus Torvalds 
12551da177e4SLinus Torvalds /*
12561da177e4SLinus Torvalds  * debug_sprintf_event:
12571da177e4SLinus Torvalds  */
__debug_sprintf_event(debug_info_t * id,int level,char * string,...)1258496da0d7SHeiko Carstens debug_entry_t *__debug_sprintf_event(debug_info_t *id, int level, char *string, ...)
12591da177e4SLinus Torvalds {
12601da177e4SLinus Torvalds 	debug_sprintf_entry_t *curr_event;
12611da177e4SLinus Torvalds 	debug_entry_t *active;
1262496da0d7SHeiko Carstens 	unsigned long flags;
1263496da0d7SHeiko Carstens 	int numargs, idx;
1264496da0d7SHeiko Carstens 	va_list ap;
12651da177e4SLinus Torvalds 
126666a464dbSMichael Holzheu 	if (!debug_active || !id->areas)
12671da177e4SLinus Torvalds 		return NULL;
12681da177e4SLinus Torvalds 	numargs = debug_count_numargs(string);
12691da177e4SLinus Torvalds 
12703ab121abSMichael Holzheu 	if (debug_critical) {
12713ab121abSMichael Holzheu 		if (!spin_trylock_irqsave(&id->lock, flags))
12723ab121abSMichael Holzheu 			return NULL;
1273496da0d7SHeiko Carstens 	} else {
12741da177e4SLinus Torvalds 		spin_lock_irqsave(&id->lock, flags);
1275496da0d7SHeiko Carstens 	}
12761da177e4SLinus Torvalds 	active = get_active_entry(id);
12771da177e4SLinus Torvalds 	curr_event = (debug_sprintf_entry_t *) DEBUG_DATA(active);
12781da177e4SLinus Torvalds 	va_start(ap, string);
12791da177e4SLinus Torvalds 	curr_event->string = string;
12801da177e4SLinus Torvalds 	for (idx = 0; idx < min(numargs, (int)(id->buf_size / sizeof(long)) - 1); idx++)
12811da177e4SLinus Torvalds 		curr_event->args[idx] = va_arg(ap, long);
12821da177e4SLinus Torvalds 	va_end(ap);
12831da177e4SLinus Torvalds 	debug_finish_entry(id, active, level, 0);
12841da177e4SLinus Torvalds 	spin_unlock_irqrestore(&id->lock, flags);
12851da177e4SLinus Torvalds 
12861da177e4SLinus Torvalds 	return active;
12871da177e4SLinus Torvalds }
1288832a7710SChristian Borntraeger EXPORT_SYMBOL(__debug_sprintf_event);
12891da177e4SLinus Torvalds 
12901da177e4SLinus Torvalds /*
12911da177e4SLinus Torvalds  * debug_sprintf_exception:
12921da177e4SLinus Torvalds  */
__debug_sprintf_exception(debug_info_t * id,int level,char * string,...)1293496da0d7SHeiko Carstens debug_entry_t *__debug_sprintf_exception(debug_info_t *id, int level, char *string, ...)
12941da177e4SLinus Torvalds {
12951da177e4SLinus Torvalds 	debug_sprintf_entry_t *curr_event;
12961da177e4SLinus Torvalds 	debug_entry_t *active;
1297496da0d7SHeiko Carstens 	unsigned long flags;
1298496da0d7SHeiko Carstens 	int numargs, idx;
1299496da0d7SHeiko Carstens 	va_list ap;
13001da177e4SLinus Torvalds 
130166a464dbSMichael Holzheu 	if (!debug_active || !id->areas)
13021da177e4SLinus Torvalds 		return NULL;
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 	numargs = debug_count_numargs(string);
13051da177e4SLinus Torvalds 
13063ab121abSMichael Holzheu 	if (debug_critical) {
13073ab121abSMichael Holzheu 		if (!spin_trylock_irqsave(&id->lock, flags))
13083ab121abSMichael Holzheu 			return NULL;
1309496da0d7SHeiko Carstens 	} else {
13101da177e4SLinus Torvalds 		spin_lock_irqsave(&id->lock, flags);
1311496da0d7SHeiko Carstens 	}
13121da177e4SLinus Torvalds 	active = get_active_entry(id);
13131da177e4SLinus Torvalds 	curr_event = (debug_sprintf_entry_t *)DEBUG_DATA(active);
13141da177e4SLinus Torvalds 	va_start(ap, string);
13151da177e4SLinus Torvalds 	curr_event->string = string;
13161da177e4SLinus Torvalds 	for (idx = 0; idx < min(numargs, (int)(id->buf_size / sizeof(long)) - 1); idx++)
13171da177e4SLinus Torvalds 		curr_event->args[idx] = va_arg(ap, long);
13181da177e4SLinus Torvalds 	va_end(ap);
13191da177e4SLinus Torvalds 	debug_finish_entry(id, active, level, 1);
13201da177e4SLinus Torvalds 	spin_unlock_irqrestore(&id->lock, flags);
13211da177e4SLinus Torvalds 
13221da177e4SLinus Torvalds 	return active;
13231da177e4SLinus Torvalds }
1324832a7710SChristian Borntraeger EXPORT_SYMBOL(__debug_sprintf_exception);
13251da177e4SLinus Torvalds 
13260328e519SSteffen Maier /**
13270328e519SSteffen Maier  * debug_register_view() - registers new debug view and creates debugfs
13280328e519SSteffen Maier  *			   dir entry
13290328e519SSteffen Maier  *
13300328e519SSteffen Maier  * @id:		handle for debug log
13310328e519SSteffen Maier  * @view:	pointer to debug view struct
13320328e519SSteffen Maier  *
13330328e519SSteffen Maier  * Return:
13340328e519SSteffen Maier  * -   0  : ok
13350328e519SSteffen Maier  * -   < 0: Error
13361da177e4SLinus Torvalds  */
debug_register_view(debug_info_t * id,struct debug_view * view)1337496da0d7SHeiko Carstens int debug_register_view(debug_info_t *id, struct debug_view *view)
13381da177e4SLinus Torvalds {
1339496da0d7SHeiko Carstens 	unsigned long flags;
1340496da0d7SHeiko Carstens 	struct dentry *pde;
1341496da0d7SHeiko Carstens 	umode_t mode;
13421da177e4SLinus Torvalds 	int rc = 0;
13431da177e4SLinus Torvalds 	int i;
13441da177e4SLinus Torvalds 
13451da177e4SLinus Torvalds 	if (!id)
13461da177e4SLinus Torvalds 		goto out;
13479637c3f3SMichael Holzheu 	mode = (id->mode | S_IFREG) & ~S_IXUGO;
13489637c3f3SMichael Holzheu 	if (!(view->prolog_proc || view->format_proc || view->header_proc))
13499637c3f3SMichael Holzheu 		mode &= ~(S_IRUSR | S_IRGRP | S_IROTH);
13509637c3f3SMichael Holzheu 	if (!view->input_proc)
13519637c3f3SMichael Holzheu 		mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
135266a464dbSMichael Holzheu 	pde = debugfs_create_file(view->name, mode, id->debugfs_root_entry,
1353942eaabdSMichael Holzheu 				  id, &debug_file_ops);
13541da177e4SLinus Torvalds 	spin_lock_irqsave(&id->lock, flags);
13551da177e4SLinus Torvalds 	for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
135666a464dbSMichael Holzheu 		if (!id->views[i])
13571da177e4SLinus Torvalds 			break;
13581da177e4SLinus Torvalds 	}
13591da177e4SLinus Torvalds 	if (i == DEBUG_MAX_VIEWS) {
13601da177e4SLinus Torvalds 		rc = -1;
136166a464dbSMichael Holzheu 	} else {
13621da177e4SLinus Torvalds 		id->views[i] = view;
136366a464dbSMichael Holzheu 		id->debugfs_entries[i] = pde;
13641da177e4SLinus Torvalds 	}
13651da177e4SLinus Torvalds 	spin_unlock_irqrestore(&id->lock, flags);
136670aa5d39SPeter Oberparleiter 	if (rc) {
136770aa5d39SPeter Oberparleiter 		pr_err("Registering view %s/%s would exceed the maximum "
136870aa5d39SPeter Oberparleiter 		       "number of views %i\n", id->name, view->name, i);
13695a334c08SMichael Holzheu 		debugfs_remove(pde);
137070aa5d39SPeter Oberparleiter 	}
13711da177e4SLinus Torvalds out:
13721da177e4SLinus Torvalds 	return rc;
13731da177e4SLinus Torvalds }
1374d5274369SHeiko Carstens EXPORT_SYMBOL(debug_register_view);
13751da177e4SLinus Torvalds 
13760328e519SSteffen Maier /**
13770328e519SSteffen Maier  * debug_unregister_view() - unregisters debug view and removes debugfs
13780328e519SSteffen Maier  *			     dir entry
13790328e519SSteffen Maier  *
13800328e519SSteffen Maier  * @id:		handle for debug log
13810328e519SSteffen Maier  * @view:	pointer to debug view struct
13820328e519SSteffen Maier  *
13830328e519SSteffen Maier  * Return:
13840328e519SSteffen Maier  * -   0  : ok
13850328e519SSteffen Maier  * -   < 0: Error
13861da177e4SLinus Torvalds  */
debug_unregister_view(debug_info_t * id,struct debug_view * view)1387496da0d7SHeiko Carstens int debug_unregister_view(debug_info_t *id, struct debug_view *view)
13881da177e4SLinus Torvalds {
13895a334c08SMichael Holzheu 	struct dentry *dentry = NULL;
13901da177e4SLinus Torvalds 	unsigned long flags;
13915a334c08SMichael Holzheu 	int i, rc = 0;
13921da177e4SLinus Torvalds 
13931da177e4SLinus Torvalds 	if (!id)
13941da177e4SLinus Torvalds 		goto out;
13951da177e4SLinus Torvalds 	spin_lock_irqsave(&id->lock, flags);
13961da177e4SLinus Torvalds 	for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
13971da177e4SLinus Torvalds 		if (id->views[i] == view)
13981da177e4SLinus Torvalds 			break;
13991da177e4SLinus Torvalds 	}
1400496da0d7SHeiko Carstens 	if (i == DEBUG_MAX_VIEWS) {
14011da177e4SLinus Torvalds 		rc = -1;
1402496da0d7SHeiko Carstens 	} else {
14035a334c08SMichael Holzheu 		dentry = id->debugfs_entries[i];
14041da177e4SLinus Torvalds 		id->views[i] = NULL;
14055a334c08SMichael Holzheu 		id->debugfs_entries[i] = NULL;
14061da177e4SLinus Torvalds 	}
14071da177e4SLinus Torvalds 	spin_unlock_irqrestore(&id->lock, flags);
14085a334c08SMichael Holzheu 	debugfs_remove(dentry);
14091da177e4SLinus Torvalds out:
14101da177e4SLinus Torvalds 	return rc;
14111da177e4SLinus Torvalds }
1412d5274369SHeiko Carstens EXPORT_SYMBOL(debug_unregister_view);
14131da177e4SLinus Torvalds 
debug_get_user_string(const char __user * user_buf,size_t user_len)1414496da0d7SHeiko Carstens static inline char *debug_get_user_string(const char __user *user_buf,
1415496da0d7SHeiko Carstens 					  size_t user_len)
141666a464dbSMichael Holzheu {
141766a464dbSMichael Holzheu 	char *buffer;
141866a464dbSMichael Holzheu 
141966a464dbSMichael Holzheu 	buffer = kmalloc(user_len + 1, GFP_KERNEL);
142066a464dbSMichael Holzheu 	if (!buffer)
142166a464dbSMichael Holzheu 		return ERR_PTR(-ENOMEM);
142266a464dbSMichael Holzheu 	if (copy_from_user(buffer, user_buf, user_len) != 0) {
142366a464dbSMichael Holzheu 		kfree(buffer);
142466a464dbSMichael Holzheu 		return ERR_PTR(-EFAULT);
142566a464dbSMichael Holzheu 	}
142666a464dbSMichael Holzheu 	/* got the string, now strip linefeed. */
142766a464dbSMichael Holzheu 	if (buffer[user_len - 1] == '\n')
142866a464dbSMichael Holzheu 		buffer[user_len - 1] = 0;
142966a464dbSMichael Holzheu 	else
143066a464dbSMichael Holzheu 		buffer[user_len] = 0;
143166a464dbSMichael Holzheu 	return buffer;
143266a464dbSMichael Holzheu }
143366a464dbSMichael Holzheu 
debug_get_uint(char * buf)1434496da0d7SHeiko Carstens static inline int debug_get_uint(char *buf)
143566a464dbSMichael Holzheu {
143666a464dbSMichael Holzheu 	int rc;
143766a464dbSMichael Holzheu 
1438e7d2860bSAndré Goddard Rosa 	buf = skip_spaces(buf);
143966a464dbSMichael Holzheu 	rc = simple_strtoul(buf, &buf, 10);
1440496da0d7SHeiko Carstens 	if (*buf)
144166a464dbSMichael Holzheu 		rc = -EINVAL;
144266a464dbSMichael Holzheu 	return rc;
144366a464dbSMichael Holzheu }
144466a464dbSMichael Holzheu 
14451da177e4SLinus Torvalds /*
14461da177e4SLinus Torvalds  * functions for debug-views
14471da177e4SLinus Torvalds  ***********************************
14481da177e4SLinus Torvalds */
14491da177e4SLinus Torvalds 
14501da177e4SLinus Torvalds /*
14511da177e4SLinus Torvalds  * prints out actual debug level
14521da177e4SLinus Torvalds  */
14531da177e4SLinus Torvalds 
debug_prolog_pages_fn(debug_info_t * id,struct debug_view * view,char * out_buf,size_t out_buf_size)1454496da0d7SHeiko Carstens static int debug_prolog_pages_fn(debug_info_t *id, struct debug_view *view,
1455897614f9SNiklas Schnelle 				 char *out_buf, size_t out_buf_size)
14561da177e4SLinus Torvalds {
1457897614f9SNiklas Schnelle 	return scnprintf(out_buf, out_buf_size, "%i\n", id->pages_per_area);
145866a464dbSMichael Holzheu }
145966a464dbSMichael Holzheu 
146066a464dbSMichael Holzheu /*
146166a464dbSMichael Holzheu  * reads new size (number of pages per debug area)
146266a464dbSMichael Holzheu  */
146366a464dbSMichael Holzheu 
debug_input_pages_fn(debug_info_t * id,struct debug_view * view,struct file * file,const char __user * user_buf,size_t user_len,loff_t * offset)1464496da0d7SHeiko Carstens static int debug_input_pages_fn(debug_info_t *id, struct debug_view *view,
146566a464dbSMichael Holzheu 				struct file *file, const char __user *user_buf,
146666a464dbSMichael Holzheu 				size_t user_len, loff_t *offset)
146766a464dbSMichael Holzheu {
146866a464dbSMichael Holzheu 	int rc, new_pages;
1469496da0d7SHeiko Carstens 	char *str;
147066a464dbSMichael Holzheu 
147166a464dbSMichael Holzheu 	if (user_len > 0x10000)
147266a464dbSMichael Holzheu 		user_len = 0x10000;
147366a464dbSMichael Holzheu 	if (*offset != 0) {
147466a464dbSMichael Holzheu 		rc = -EPIPE;
147566a464dbSMichael Holzheu 		goto out;
147666a464dbSMichael Holzheu 	}
147766a464dbSMichael Holzheu 	str = debug_get_user_string(user_buf, user_len);
147866a464dbSMichael Holzheu 	if (IS_ERR(str)) {
147966a464dbSMichael Holzheu 		rc = PTR_ERR(str);
148066a464dbSMichael Holzheu 		goto out;
148166a464dbSMichael Holzheu 	}
148266a464dbSMichael Holzheu 	new_pages = debug_get_uint(str);
148366a464dbSMichael Holzheu 	if (new_pages < 0) {
148466a464dbSMichael Holzheu 		rc = -EINVAL;
148566a464dbSMichael Holzheu 		goto free_str;
148666a464dbSMichael Holzheu 	}
148766a464dbSMichael Holzheu 	rc = debug_set_size(id, id->nr_areas, new_pages);
148866a464dbSMichael Holzheu 	if (rc != 0) {
148966a464dbSMichael Holzheu 		rc = -EINVAL;
149066a464dbSMichael Holzheu 		goto free_str;
149166a464dbSMichael Holzheu 	}
149266a464dbSMichael Holzheu 	rc = user_len;
149366a464dbSMichael Holzheu free_str:
149466a464dbSMichael Holzheu 	kfree(str);
149566a464dbSMichael Holzheu out:
149666a464dbSMichael Holzheu 	*offset += user_len;
149766a464dbSMichael Holzheu 	return rc;		/* number of input characters */
149866a464dbSMichael Holzheu }
149966a464dbSMichael Holzheu 
150066a464dbSMichael Holzheu /*
150166a464dbSMichael Holzheu  * prints out actual debug level
150266a464dbSMichael Holzheu  */
debug_prolog_level_fn(debug_info_t * id,struct debug_view * view,char * out_buf,size_t out_buf_size)1503496da0d7SHeiko Carstens static int debug_prolog_level_fn(debug_info_t *id, struct debug_view *view,
1504897614f9SNiklas Schnelle 				 char *out_buf, size_t out_buf_size)
150566a464dbSMichael Holzheu {
15061da177e4SLinus Torvalds 	int rc = 0;
15071da177e4SLinus Torvalds 
1508496da0d7SHeiko Carstens 	if (id->level == DEBUG_OFF_LEVEL)
1509897614f9SNiklas Schnelle 		rc = scnprintf(out_buf, out_buf_size, "-\n");
1510496da0d7SHeiko Carstens 	else
1511897614f9SNiklas Schnelle 		rc = scnprintf(out_buf, out_buf_size, "%i\n", id->level);
15121da177e4SLinus Torvalds 	return rc;
15131da177e4SLinus Torvalds }
15141da177e4SLinus Torvalds 
15151da177e4SLinus Torvalds /*
15161da177e4SLinus Torvalds  * reads new debug level
15171da177e4SLinus Torvalds  */
debug_input_level_fn(debug_info_t * id,struct debug_view * view,struct file * file,const char __user * user_buf,size_t user_len,loff_t * offset)1518496da0d7SHeiko Carstens static int debug_input_level_fn(debug_info_t *id, struct debug_view *view,
15191da177e4SLinus Torvalds 				struct file *file, const char __user *user_buf,
152066a464dbSMichael Holzheu 				size_t user_len, loff_t *offset)
15211da177e4SLinus Torvalds {
152266a464dbSMichael Holzheu 	int rc, new_level;
1523496da0d7SHeiko Carstens 	char *str;
15241da177e4SLinus Torvalds 
152566a464dbSMichael Holzheu 	if (user_len > 0x10000)
152666a464dbSMichael Holzheu 		user_len = 0x10000;
152766a464dbSMichael Holzheu 	if (*offset != 0) {
152866a464dbSMichael Holzheu 		rc = -EPIPE;
15291da177e4SLinus Torvalds 		goto out;
15301da177e4SLinus Torvalds 	}
153166a464dbSMichael Holzheu 	str = debug_get_user_string(user_buf, user_len);
153266a464dbSMichael Holzheu 	if (IS_ERR(str)) {
153366a464dbSMichael Holzheu 		rc = PTR_ERR(str);
153466a464dbSMichael Holzheu 		goto out;
153566a464dbSMichael Holzheu 	}
153666a464dbSMichael Holzheu 	if (str[0] == '-') {
15371da177e4SLinus Torvalds 		debug_set_level(id, DEBUG_OFF_LEVEL);
153866a464dbSMichael Holzheu 		rc = user_len;
153966a464dbSMichael Holzheu 		goto free_str;
15401da177e4SLinus Torvalds 	} else {
154166a464dbSMichael Holzheu 		new_level = debug_get_uint(str);
15421da177e4SLinus Torvalds 	}
154366a464dbSMichael Holzheu 	if (new_level < 0) {
1544baebc70aSJoe Perches 		pr_warn("%s is not a valid level for a debug feature\n", str);
154566a464dbSMichael Holzheu 		rc = -EINVAL;
154666a464dbSMichael Holzheu 	} else {
154766a464dbSMichael Holzheu 		debug_set_level(id, new_level);
154866a464dbSMichael Holzheu 		rc = user_len;
154966a464dbSMichael Holzheu 	}
155066a464dbSMichael Holzheu free_str:
155166a464dbSMichael Holzheu 	kfree(str);
15521da177e4SLinus Torvalds out:
155366a464dbSMichael Holzheu 	*offset += user_len;
15541da177e4SLinus Torvalds 	return rc;		/* number of input characters */
15551da177e4SLinus Torvalds }
15561da177e4SLinus Torvalds 
15571da177e4SLinus Torvalds /*
15581da177e4SLinus Torvalds  * flushes debug areas
15591da177e4SLinus Torvalds  */
debug_flush(debug_info_t * id,int area)15602b67fc46SHeiko Carstens static void debug_flush(debug_info_t *id, int area)
15611da177e4SLinus Torvalds {
15621da177e4SLinus Torvalds 	unsigned long flags;
156366a464dbSMichael Holzheu 	int i, j;
15641da177e4SLinus Torvalds 
156566a464dbSMichael Holzheu 	if (!id || !id->areas)
15661da177e4SLinus Torvalds 		return;
15671da177e4SLinus Torvalds 	spin_lock_irqsave(&id->lock, flags);
15681da177e4SLinus Torvalds 	if (area == DEBUG_FLUSH_ALL) {
15691da177e4SLinus Torvalds 		id->active_area = 0;
157066a464dbSMichael Holzheu 		memset(id->active_entries, 0, id->nr_areas * sizeof(int));
157166a464dbSMichael Holzheu 		for (i = 0; i < id->nr_areas; i++) {
157266a464dbSMichael Holzheu 			id->active_pages[i] = 0;
1573496da0d7SHeiko Carstens 			for (j = 0; j < id->pages_per_area; j++)
157466a464dbSMichael Holzheu 				memset(id->areas[i][j], 0, PAGE_SIZE);
157566a464dbSMichael Holzheu 		}
15761da177e4SLinus Torvalds 	} else if (area >= 0 && area < id->nr_areas) {
157766a464dbSMichael Holzheu 		id->active_entries[area] = 0;
157866a464dbSMichael Holzheu 		id->active_pages[area] = 0;
1579496da0d7SHeiko Carstens 		for (i = 0; i < id->pages_per_area; i++)
158066a464dbSMichael Holzheu 			memset(id->areas[area][i], 0, PAGE_SIZE);
158166a464dbSMichael Holzheu 	}
15821da177e4SLinus Torvalds 	spin_unlock_irqrestore(&id->lock, flags);
15831da177e4SLinus Torvalds }
15841da177e4SLinus Torvalds 
15851da177e4SLinus Torvalds /*
15861da177e4SLinus Torvalds  * view function: flushes debug areas
15871da177e4SLinus Torvalds  */
debug_input_flush_fn(debug_info_t * id,struct debug_view * view,struct file * file,const char __user * user_buf,size_t user_len,loff_t * offset)1588496da0d7SHeiko Carstens static int debug_input_flush_fn(debug_info_t *id, struct debug_view *view,
15891da177e4SLinus Torvalds 				struct file *file, const char __user *user_buf,
159066a464dbSMichael Holzheu 				size_t user_len, loff_t *offset)
15911da177e4SLinus Torvalds {
15921da177e4SLinus Torvalds 	char input_buf[1];
159366a464dbSMichael Holzheu 	int rc = user_len;
15941da177e4SLinus Torvalds 
159566a464dbSMichael Holzheu 	if (user_len > 0x10000)
159666a464dbSMichael Holzheu 		user_len = 0x10000;
159766a464dbSMichael Holzheu 	if (*offset != 0) {
159866a464dbSMichael Holzheu 		rc = -EPIPE;
15991da177e4SLinus Torvalds 		goto out;
160066a464dbSMichael Holzheu 	}
16011da177e4SLinus Torvalds 	if (copy_from_user(input_buf, user_buf, 1)) {
16021da177e4SLinus Torvalds 		rc = -EFAULT;
16031da177e4SLinus Torvalds 		goto out;
16041da177e4SLinus Torvalds 	}
16051da177e4SLinus Torvalds 	if (input_buf[0] == '-') {
16061da177e4SLinus Torvalds 		debug_flush(id, DEBUG_FLUSH_ALL);
16071da177e4SLinus Torvalds 		goto out;
16081da177e4SLinus Torvalds 	}
16091da177e4SLinus Torvalds 	if (isdigit(input_buf[0])) {
16101da177e4SLinus Torvalds 		int area = ((int) input_buf[0] - (int) '0');
1611496da0d7SHeiko Carstens 
16121da177e4SLinus Torvalds 		debug_flush(id, area);
16131da177e4SLinus Torvalds 		goto out;
16141da177e4SLinus Torvalds 	}
16151da177e4SLinus Torvalds 
1616c5612c19SMichael Holzheu 	pr_info("Flushing debug data failed because %c is not a valid "
1617c5612c19SMichael Holzheu 		 "area\n", input_buf[0]);
16181da177e4SLinus Torvalds 
16191da177e4SLinus Torvalds out:
162066a464dbSMichael Holzheu 	*offset += user_len;
16211da177e4SLinus Torvalds 	return rc;		/* number of input characters */
16221da177e4SLinus Torvalds }
16231da177e4SLinus Torvalds 
16241da177e4SLinus Torvalds /*
16251da177e4SLinus Torvalds  * prints debug data in hex/ascii format
16261da177e4SLinus Torvalds  */
debug_hex_ascii_format_fn(debug_info_t * id,struct debug_view * view,char * out_buf,size_t out_buf_size,const char * in_buf)1627496da0d7SHeiko Carstens static int debug_hex_ascii_format_fn(debug_info_t *id, struct debug_view *view,
1628897614f9SNiklas Schnelle 				     char *out_buf, size_t out_buf_size, const char *in_buf)
16291da177e4SLinus Torvalds {
16301da177e4SLinus Torvalds 	int i, rc = 0;
16311da177e4SLinus Torvalds 
1632897614f9SNiklas Schnelle 	for (i = 0; i < id->buf_size; i++) {
1633897614f9SNiklas Schnelle 		rc += scnprintf(out_buf + rc, out_buf_size - rc,
1634897614f9SNiklas Schnelle 				"%02x ", ((unsigned char *)in_buf)[i]);
1635897614f9SNiklas Schnelle 	}
1636897614f9SNiklas Schnelle 	rc += scnprintf(out_buf + rc, out_buf_size - rc, "| ");
16371da177e4SLinus Torvalds 	for (i = 0; i < id->buf_size; i++) {
16381da177e4SLinus Torvalds 		unsigned char c = in_buf[i];
1639496da0d7SHeiko Carstens 
16403ab121abSMichael Holzheu 		if (isascii(c) && isprint(c))
1641897614f9SNiklas Schnelle 			rc += scnprintf(out_buf + rc, out_buf_size - rc, "%c", c);
16423ab121abSMichael Holzheu 		else
1643897614f9SNiklas Schnelle 			rc += scnprintf(out_buf + rc, out_buf_size - rc, ".");
16441da177e4SLinus Torvalds 	}
1645897614f9SNiklas Schnelle 	rc += scnprintf(out_buf + rc, out_buf_size - rc, "\n");
16461da177e4SLinus Torvalds 	return rc;
16471da177e4SLinus Torvalds }
16481da177e4SLinus Torvalds 
16491da177e4SLinus Torvalds /*
16501da177e4SLinus Torvalds  * prints header for debug entry
16511da177e4SLinus Torvalds  */
debug_dflt_header_fn(debug_info_t * id,struct debug_view * view,int area,debug_entry_t * entry,char * out_buf,size_t out_buf_size)1652496da0d7SHeiko Carstens int debug_dflt_header_fn(debug_info_t *id, struct debug_view *view,
1653897614f9SNiklas Schnelle 			 int area, debug_entry_t *entry, char *out_buf,
1654897614f9SNiklas Schnelle 			 size_t out_buf_size)
16551da177e4SLinus Torvalds {
16560990d836SMikhail Zaslonko 	unsigned long sec, usec;
16571da177e4SLinus Torvalds 	unsigned long caller;
16581da177e4SLinus Torvalds 	unsigned int level;
1659496da0d7SHeiko Carstens 	char *except_str;
1660496da0d7SHeiko Carstens 	int rc = 0;
16611da177e4SLinus Torvalds 
16620990d836SMikhail Zaslonko 	level = entry->level;
16630990d836SMikhail Zaslonko 	sec = entry->clock;
1664ea417aa8SMartin Schwidefsky 	usec = do_div(sec, USEC_PER_SEC);
16651da177e4SLinus Torvalds 
16660990d836SMikhail Zaslonko 	if (entry->exception)
16671da177e4SLinus Torvalds 		except_str = "*";
16681da177e4SLinus Torvalds 	else
16691da177e4SLinus Torvalds 		except_str = "-";
16709cb1ccecSHeiko Carstens 	caller = (unsigned long) entry->caller;
1671897614f9SNiklas Schnelle 	rc += scnprintf(out_buf, out_buf_size, "%02i %011ld:%06lu %1u %1s %04u %px  ",
1672ea417aa8SMartin Schwidefsky 			area, sec, usec, level, except_str,
16730990d836SMikhail Zaslonko 			entry->cpu, (void *)caller);
16741da177e4SLinus Torvalds 	return rc;
16751da177e4SLinus Torvalds }
1676d5274369SHeiko Carstens EXPORT_SYMBOL(debug_dflt_header_fn);
16771da177e4SLinus Torvalds 
16781da177e4SLinus Torvalds /*
1679e6ebf0d6SHeiko Carstens  * prints debug data sprintf-formatted:
16801da177e4SLinus Torvalds  * debug_sprinf_event/exception calls must be used together with this view
16811da177e4SLinus Torvalds  */
16821da177e4SLinus Torvalds 
16831da177e4SLinus Torvalds #define DEBUG_SPRINTF_MAX_ARGS 10
16841da177e4SLinus Torvalds 
debug_sprintf_format_fn(debug_info_t * id,struct debug_view * view,char * out_buf,size_t out_buf_size,const char * inbuf)16854c41a48fSNiklas Schnelle int debug_sprintf_format_fn(debug_info_t *id, struct debug_view *view,
1686897614f9SNiklas Schnelle 			    char *out_buf, size_t out_buf_size, const char *inbuf)
16871da177e4SLinus Torvalds {
1688a78c2e31SHeiko Carstens 	debug_sprintf_entry_t *curr_event = (debug_sprintf_entry_t *)inbuf;
16891da177e4SLinus Torvalds 	int num_longs, num_used_args = 0, i, rc = 0;
16901da177e4SLinus Torvalds 	int index[DEBUG_SPRINTF_MAX_ARGS];
16911da177e4SLinus Torvalds 
16921da177e4SLinus Torvalds 	/* count of longs fit into one entry */
16931da177e4SLinus Torvalds 	num_longs = id->buf_size / sizeof(long);
16941da177e4SLinus Torvalds 
16951da177e4SLinus Torvalds 	if (num_longs < 1)
16961da177e4SLinus Torvalds 		goto out; /* bufsize of entry too small */
16971da177e4SLinus Torvalds 	if (num_longs == 1) {
16981da177e4SLinus Torvalds 		/* no args, we use only the string */
1699897614f9SNiklas Schnelle 		rc = strscpy(out_buf, curr_event->string, out_buf_size);
1700897614f9SNiklas Schnelle 		if (rc == -E2BIG)
1701897614f9SNiklas Schnelle 			rc = out_buf_size;
17021da177e4SLinus Torvalds 		goto out;
17031da177e4SLinus Torvalds 	}
17041da177e4SLinus Torvalds 
17051da177e4SLinus Torvalds 	/* number of arguments used for sprintf (without the format string) */
17061da177e4SLinus Torvalds 	num_used_args = min(DEBUG_SPRINTF_MAX_ARGS, (num_longs - 1));
17071da177e4SLinus Torvalds 
17081da177e4SLinus Torvalds 	memset(index, 0, DEBUG_SPRINTF_MAX_ARGS * sizeof(int));
17091da177e4SLinus Torvalds 
17101da177e4SLinus Torvalds 	for (i = 0; i < num_used_args; i++)
17111da177e4SLinus Torvalds 		index[i] = i;
17121da177e4SLinus Torvalds 
1713897614f9SNiklas Schnelle 	rc = scnprintf(out_buf, out_buf_size,
1714897614f9SNiklas Schnelle 		       curr_event->string, curr_event->args[index[0]],
17151da177e4SLinus Torvalds 		       curr_event->args[index[1]], curr_event->args[index[2]],
17161da177e4SLinus Torvalds 		       curr_event->args[index[3]], curr_event->args[index[4]],
17171da177e4SLinus Torvalds 		       curr_event->args[index[5]], curr_event->args[index[6]],
17181da177e4SLinus Torvalds 		       curr_event->args[index[7]], curr_event->args[index[8]],
17191da177e4SLinus Torvalds 		       curr_event->args[index[9]]);
17201da177e4SLinus Torvalds out:
17211da177e4SLinus Torvalds 	return rc;
17221da177e4SLinus Torvalds }
17234c41a48fSNiklas Schnelle EXPORT_SYMBOL(debug_sprintf_format_fn);
17241da177e4SLinus Torvalds 
17251da177e4SLinus Torvalds /*
1726d5274369SHeiko Carstens  * debug_init:
1727d5274369SHeiko Carstens  * - is called exactly once to initialize the debug feature
17281da177e4SLinus Torvalds  */
debug_init(void)1729d5274369SHeiko Carstens static int __init debug_init(void)
17301da177e4SLinus Torvalds {
1731751e2407SLuis Chamberlain 	s390dbf_sysctl_header = register_sysctl("s390dbf", s390dbf_table);
1732d5274369SHeiko Carstens 	mutex_lock(&debug_mutex);
1733d5274369SHeiko Carstens 	debug_debugfs_root_entry = debugfs_create_dir(DEBUG_DIR_ROOT, NULL);
1734d5274369SHeiko Carstens 	initialized = 1;
1735d5274369SHeiko Carstens 	mutex_unlock(&debug_mutex);
1736d5274369SHeiko Carstens 	return 0;
17371da177e4SLinus Torvalds }
173866a464dbSMichael Holzheu postcore_initcall(debug_init);
1739