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