162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * lib/dynamic_debug.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * make pr_debug()/dev_dbg() calls runtime configurable based upon their
562306a36Sopenharmony_ci * source module.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2008 Jason Baron <jbaron@redhat.com>
862306a36Sopenharmony_ci * By Greg Banks <gnb@melbourne.sgi.com>
962306a36Sopenharmony_ci * Copyright (c) 2008 Silicon Graphics Inc.  All Rights Reserved.
1062306a36Sopenharmony_ci * Copyright (C) 2011 Bart Van Assche.  All Rights Reserved.
1162306a36Sopenharmony_ci * Copyright (C) 2013 Du, Changbin <changbin.du@gmail.com>
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define pr_fmt(fmt) "dyndbg: " fmt
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/moduleparam.h>
1962306a36Sopenharmony_ci#include <linux/kallsyms.h>
2062306a36Sopenharmony_ci#include <linux/types.h>
2162306a36Sopenharmony_ci#include <linux/mutex.h>
2262306a36Sopenharmony_ci#include <linux/proc_fs.h>
2362306a36Sopenharmony_ci#include <linux/seq_file.h>
2462306a36Sopenharmony_ci#include <linux/list.h>
2562306a36Sopenharmony_ci#include <linux/sysctl.h>
2662306a36Sopenharmony_ci#include <linux/ctype.h>
2762306a36Sopenharmony_ci#include <linux/string.h>
2862306a36Sopenharmony_ci#include <linux/parser.h>
2962306a36Sopenharmony_ci#include <linux/string_helpers.h>
3062306a36Sopenharmony_ci#include <linux/uaccess.h>
3162306a36Sopenharmony_ci#include <linux/dynamic_debug.h>
3262306a36Sopenharmony_ci#include <linux/debugfs.h>
3362306a36Sopenharmony_ci#include <linux/slab.h>
3462306a36Sopenharmony_ci#include <linux/jump_label.h>
3562306a36Sopenharmony_ci#include <linux/hardirq.h>
3662306a36Sopenharmony_ci#include <linux/sched.h>
3762306a36Sopenharmony_ci#include <linux/device.h>
3862306a36Sopenharmony_ci#include <linux/netdevice.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include <rdma/ib_verbs.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciextern struct _ddebug __start___dyndbg[];
4362306a36Sopenharmony_ciextern struct _ddebug __stop___dyndbg[];
4462306a36Sopenharmony_ciextern struct ddebug_class_map __start___dyndbg_classes[];
4562306a36Sopenharmony_ciextern struct ddebug_class_map __stop___dyndbg_classes[];
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct ddebug_table {
4862306a36Sopenharmony_ci	struct list_head link, maps;
4962306a36Sopenharmony_ci	const char *mod_name;
5062306a36Sopenharmony_ci	unsigned int num_ddebugs;
5162306a36Sopenharmony_ci	struct _ddebug *ddebugs;
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct ddebug_query {
5562306a36Sopenharmony_ci	const char *filename;
5662306a36Sopenharmony_ci	const char *module;
5762306a36Sopenharmony_ci	const char *function;
5862306a36Sopenharmony_ci	const char *format;
5962306a36Sopenharmony_ci	const char *class_string;
6062306a36Sopenharmony_ci	unsigned int first_lineno, last_lineno;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct ddebug_iter {
6462306a36Sopenharmony_ci	struct ddebug_table *table;
6562306a36Sopenharmony_ci	int idx;
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct flag_settings {
6962306a36Sopenharmony_ci	unsigned int flags;
7062306a36Sopenharmony_ci	unsigned int mask;
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic DEFINE_MUTEX(ddebug_lock);
7462306a36Sopenharmony_cistatic LIST_HEAD(ddebug_tables);
7562306a36Sopenharmony_cistatic int verbose;
7662306a36Sopenharmony_cimodule_param(verbose, int, 0644);
7762306a36Sopenharmony_ciMODULE_PARM_DESC(verbose, " dynamic_debug/control processing "
7862306a36Sopenharmony_ci		 "( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)");
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* Return the path relative to source root */
8162306a36Sopenharmony_cistatic inline const char *trim_prefix(const char *path)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int skip = strlen(__FILE__) - strlen("lib/dynamic_debug.c");
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (strncmp(path, __FILE__, skip))
8662306a36Sopenharmony_ci		skip = 0; /* prefix mismatch, don't skip */
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return path + skip;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic const struct { unsigned flag:8; char opt_char; } opt_array[] = {
9262306a36Sopenharmony_ci	{ _DPRINTK_FLAGS_PRINT, 'p' },
9362306a36Sopenharmony_ci	{ _DPRINTK_FLAGS_INCL_MODNAME, 'm' },
9462306a36Sopenharmony_ci	{ _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' },
9562306a36Sopenharmony_ci	{ _DPRINTK_FLAGS_INCL_SOURCENAME, 's' },
9662306a36Sopenharmony_ci	{ _DPRINTK_FLAGS_INCL_LINENO, 'l' },
9762306a36Sopenharmony_ci	{ _DPRINTK_FLAGS_INCL_TID, 't' },
9862306a36Sopenharmony_ci	{ _DPRINTK_FLAGS_NONE, '_' },
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistruct flagsbuf { char buf[ARRAY_SIZE(opt_array)+1]; };
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* format a string into buf[] which describes the _ddebug's flags */
10462306a36Sopenharmony_cistatic char *ddebug_describe_flags(unsigned int flags, struct flagsbuf *fb)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	char *p = fb->buf;
10762306a36Sopenharmony_ci	int i;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(opt_array); ++i)
11062306a36Sopenharmony_ci		if (flags & opt_array[i].flag)
11162306a36Sopenharmony_ci			*p++ = opt_array[i].opt_char;
11262306a36Sopenharmony_ci	if (p == fb->buf)
11362306a36Sopenharmony_ci		*p++ = '_';
11462306a36Sopenharmony_ci	*p = '\0';
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return fb->buf;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#define vnpr_info(lvl, fmt, ...)				\
12062306a36Sopenharmony_cido {								\
12162306a36Sopenharmony_ci	if (verbose >= lvl)					\
12262306a36Sopenharmony_ci		pr_info(fmt, ##__VA_ARGS__);			\
12362306a36Sopenharmony_ci} while (0)
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci#define vpr_info(fmt, ...)	vnpr_info(1, fmt, ##__VA_ARGS__)
12662306a36Sopenharmony_ci#define v2pr_info(fmt, ...)	vnpr_info(2, fmt, ##__VA_ARGS__)
12762306a36Sopenharmony_ci#define v3pr_info(fmt, ...)	vnpr_info(3, fmt, ##__VA_ARGS__)
12862306a36Sopenharmony_ci#define v4pr_info(fmt, ...)	vnpr_info(4, fmt, ##__VA_ARGS__)
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void vpr_info_dq(const struct ddebug_query *query, const char *msg)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	/* trim any trailing newlines */
13362306a36Sopenharmony_ci	int fmtlen = 0;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (query->format) {
13662306a36Sopenharmony_ci		fmtlen = strlen(query->format);
13762306a36Sopenharmony_ci		while (fmtlen && query->format[fmtlen - 1] == '\n')
13862306a36Sopenharmony_ci			fmtlen--;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	v3pr_info("%s: func=\"%s\" file=\"%s\" module=\"%s\" format=\"%.*s\" lineno=%u-%u class=%s\n",
14262306a36Sopenharmony_ci		  msg,
14362306a36Sopenharmony_ci		  query->function ?: "",
14462306a36Sopenharmony_ci		  query->filename ?: "",
14562306a36Sopenharmony_ci		  query->module ?: "",
14662306a36Sopenharmony_ci		  fmtlen, query->format ?: "",
14762306a36Sopenharmony_ci		  query->first_lineno, query->last_lineno, query->class_string);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table const *dt,
15162306a36Sopenharmony_ci							  const char *class_string, int *class_id)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct ddebug_class_map *map;
15462306a36Sopenharmony_ci	int idx;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	list_for_each_entry(map, &dt->maps, link) {
15762306a36Sopenharmony_ci		idx = match_string(map->class_names, map->length, class_string);
15862306a36Sopenharmony_ci		if (idx >= 0) {
15962306a36Sopenharmony_ci			*class_id = idx + map->base;
16062306a36Sopenharmony_ci			return map;
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci	*class_id = -ENOENT;
16462306a36Sopenharmony_ci	return NULL;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci#define __outvar /* filled by callee */
16862306a36Sopenharmony_ci/*
16962306a36Sopenharmony_ci * Search the tables for _ddebug's which match the given `query' and
17062306a36Sopenharmony_ci * apply the `flags' and `mask' to them.  Returns number of matching
17162306a36Sopenharmony_ci * callsites, normally the same as number of changes.  If verbose,
17262306a36Sopenharmony_ci * logs the changes.  Takes ddebug_lock.
17362306a36Sopenharmony_ci */
17462306a36Sopenharmony_cistatic int ddebug_change(const struct ddebug_query *query,
17562306a36Sopenharmony_ci			 struct flag_settings *modifiers)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	int i;
17862306a36Sopenharmony_ci	struct ddebug_table *dt;
17962306a36Sopenharmony_ci	unsigned int newflags;
18062306a36Sopenharmony_ci	unsigned int nfound = 0;
18162306a36Sopenharmony_ci	struct flagsbuf fbuf, nbuf;
18262306a36Sopenharmony_ci	struct ddebug_class_map *map = NULL;
18362306a36Sopenharmony_ci	int __outvar valid_class;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* search for matching ddebugs */
18662306a36Sopenharmony_ci	mutex_lock(&ddebug_lock);
18762306a36Sopenharmony_ci	list_for_each_entry(dt, &ddebug_tables, link) {
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		/* match against the module name */
19062306a36Sopenharmony_ci		if (query->module &&
19162306a36Sopenharmony_ci		    !match_wildcard(query->module, dt->mod_name))
19262306a36Sopenharmony_ci			continue;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		if (query->class_string) {
19562306a36Sopenharmony_ci			map = ddebug_find_valid_class(dt, query->class_string, &valid_class);
19662306a36Sopenharmony_ci			if (!map)
19762306a36Sopenharmony_ci				continue;
19862306a36Sopenharmony_ci		} else {
19962306a36Sopenharmony_ci			/* constrain query, do not touch class'd callsites */
20062306a36Sopenharmony_ci			valid_class = _DPRINTK_CLASS_DFLT;
20162306a36Sopenharmony_ci		}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		for (i = 0; i < dt->num_ddebugs; i++) {
20462306a36Sopenharmony_ci			struct _ddebug *dp = &dt->ddebugs[i];
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci			/* match site against query-class */
20762306a36Sopenharmony_ci			if (dp->class_id != valid_class)
20862306a36Sopenharmony_ci				continue;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci			/* match against the source filename */
21162306a36Sopenharmony_ci			if (query->filename &&
21262306a36Sopenharmony_ci			    !match_wildcard(query->filename, dp->filename) &&
21362306a36Sopenharmony_ci			    !match_wildcard(query->filename,
21462306a36Sopenharmony_ci					   kbasename(dp->filename)) &&
21562306a36Sopenharmony_ci			    !match_wildcard(query->filename,
21662306a36Sopenharmony_ci					   trim_prefix(dp->filename)))
21762306a36Sopenharmony_ci				continue;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci			/* match against the function */
22062306a36Sopenharmony_ci			if (query->function &&
22162306a36Sopenharmony_ci			    !match_wildcard(query->function, dp->function))
22262306a36Sopenharmony_ci				continue;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci			/* match against the format */
22562306a36Sopenharmony_ci			if (query->format) {
22662306a36Sopenharmony_ci				if (*query->format == '^') {
22762306a36Sopenharmony_ci					char *p;
22862306a36Sopenharmony_ci					/* anchored search. match must be at beginning */
22962306a36Sopenharmony_ci					p = strstr(dp->format, query->format+1);
23062306a36Sopenharmony_ci					if (p != dp->format)
23162306a36Sopenharmony_ci						continue;
23262306a36Sopenharmony_ci				} else if (!strstr(dp->format, query->format))
23362306a36Sopenharmony_ci					continue;
23462306a36Sopenharmony_ci			}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci			/* match against the line number range */
23762306a36Sopenharmony_ci			if (query->first_lineno &&
23862306a36Sopenharmony_ci			    dp->lineno < query->first_lineno)
23962306a36Sopenharmony_ci				continue;
24062306a36Sopenharmony_ci			if (query->last_lineno &&
24162306a36Sopenharmony_ci			    dp->lineno > query->last_lineno)
24262306a36Sopenharmony_ci				continue;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci			nfound++;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci			newflags = (dp->flags & modifiers->mask) | modifiers->flags;
24762306a36Sopenharmony_ci			if (newflags == dp->flags)
24862306a36Sopenharmony_ci				continue;
24962306a36Sopenharmony_ci#ifdef CONFIG_JUMP_LABEL
25062306a36Sopenharmony_ci			if (dp->flags & _DPRINTK_FLAGS_PRINT) {
25162306a36Sopenharmony_ci				if (!(newflags & _DPRINTK_FLAGS_PRINT))
25262306a36Sopenharmony_ci					static_branch_disable(&dp->key.dd_key_true);
25362306a36Sopenharmony_ci			} else if (newflags & _DPRINTK_FLAGS_PRINT) {
25462306a36Sopenharmony_ci				static_branch_enable(&dp->key.dd_key_true);
25562306a36Sopenharmony_ci			}
25662306a36Sopenharmony_ci#endif
25762306a36Sopenharmony_ci			v4pr_info("changed %s:%d [%s]%s %s => %s\n",
25862306a36Sopenharmony_ci				  trim_prefix(dp->filename), dp->lineno,
25962306a36Sopenharmony_ci				  dt->mod_name, dp->function,
26062306a36Sopenharmony_ci				  ddebug_describe_flags(dp->flags, &fbuf),
26162306a36Sopenharmony_ci				  ddebug_describe_flags(newflags, &nbuf));
26262306a36Sopenharmony_ci			dp->flags = newflags;
26362306a36Sopenharmony_ci		}
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci	mutex_unlock(&ddebug_lock);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (!nfound && verbose)
26862306a36Sopenharmony_ci		pr_info("no matches for query\n");
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return nfound;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci/*
27462306a36Sopenharmony_ci * Split the buffer `buf' into space-separated words.
27562306a36Sopenharmony_ci * Handles simple " and ' quoting, i.e. without nested,
27662306a36Sopenharmony_ci * embedded or escaped \".  Return the number of words
27762306a36Sopenharmony_ci * or <0 on error.
27862306a36Sopenharmony_ci */
27962306a36Sopenharmony_cistatic int ddebug_tokenize(char *buf, char *words[], int maxwords)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	int nwords = 0;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	while (*buf) {
28462306a36Sopenharmony_ci		char *end;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		/* Skip leading whitespace */
28762306a36Sopenharmony_ci		buf = skip_spaces(buf);
28862306a36Sopenharmony_ci		if (!*buf)
28962306a36Sopenharmony_ci			break;	/* oh, it was trailing whitespace */
29062306a36Sopenharmony_ci		if (*buf == '#')
29162306a36Sopenharmony_ci			break;	/* token starts comment, skip rest of line */
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		/* find `end' of word, whitespace separated or quoted */
29462306a36Sopenharmony_ci		if (*buf == '"' || *buf == '\'') {
29562306a36Sopenharmony_ci			int quote = *buf++;
29662306a36Sopenharmony_ci			for (end = buf; *end && *end != quote; end++)
29762306a36Sopenharmony_ci				;
29862306a36Sopenharmony_ci			if (!*end) {
29962306a36Sopenharmony_ci				pr_err("unclosed quote: %s\n", buf);
30062306a36Sopenharmony_ci				return -EINVAL;	/* unclosed quote */
30162306a36Sopenharmony_ci			}
30262306a36Sopenharmony_ci		} else {
30362306a36Sopenharmony_ci			for (end = buf; *end && !isspace(*end); end++)
30462306a36Sopenharmony_ci				;
30562306a36Sopenharmony_ci			BUG_ON(end == buf);
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		/* `buf' is start of word, `end' is one past its end */
30962306a36Sopenharmony_ci		if (nwords == maxwords) {
31062306a36Sopenharmony_ci			pr_err("too many words, legal max <=%d\n", maxwords);
31162306a36Sopenharmony_ci			return -EINVAL;	/* ran out of words[] before bytes */
31262306a36Sopenharmony_ci		}
31362306a36Sopenharmony_ci		if (*end)
31462306a36Sopenharmony_ci			*end++ = '\0';	/* terminate the word */
31562306a36Sopenharmony_ci		words[nwords++] = buf;
31662306a36Sopenharmony_ci		buf = end;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (verbose >= 3) {
32062306a36Sopenharmony_ci		int i;
32162306a36Sopenharmony_ci		pr_info("split into words:");
32262306a36Sopenharmony_ci		for (i = 0; i < nwords; i++)
32362306a36Sopenharmony_ci			pr_cont(" \"%s\"", words[i]);
32462306a36Sopenharmony_ci		pr_cont("\n");
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return nwords;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci/*
33162306a36Sopenharmony_ci * Parse a single line number.  Note that the empty string ""
33262306a36Sopenharmony_ci * is treated as a special case and converted to zero, which
33362306a36Sopenharmony_ci * is later treated as a "don't care" value.
33462306a36Sopenharmony_ci */
33562306a36Sopenharmony_cistatic inline int parse_lineno(const char *str, unsigned int *val)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	BUG_ON(str == NULL);
33862306a36Sopenharmony_ci	if (*str == '\0') {
33962306a36Sopenharmony_ci		*val = 0;
34062306a36Sopenharmony_ci		return 0;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci	if (kstrtouint(str, 10, val) < 0) {
34362306a36Sopenharmony_ci		pr_err("bad line-number: %s\n", str);
34462306a36Sopenharmony_ci		return -EINVAL;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci	return 0;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic int parse_linerange(struct ddebug_query *query, const char *first)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	char *last = strchr(first, '-');
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (query->first_lineno || query->last_lineno) {
35462306a36Sopenharmony_ci		pr_err("match-spec: line used 2x\n");
35562306a36Sopenharmony_ci		return -EINVAL;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	if (last)
35862306a36Sopenharmony_ci		*last++ = '\0';
35962306a36Sopenharmony_ci	if (parse_lineno(first, &query->first_lineno) < 0)
36062306a36Sopenharmony_ci		return -EINVAL;
36162306a36Sopenharmony_ci	if (last) {
36262306a36Sopenharmony_ci		/* range <first>-<last> */
36362306a36Sopenharmony_ci		if (parse_lineno(last, &query->last_lineno) < 0)
36462306a36Sopenharmony_ci			return -EINVAL;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		/* special case for last lineno not specified */
36762306a36Sopenharmony_ci		if (query->last_lineno == 0)
36862306a36Sopenharmony_ci			query->last_lineno = UINT_MAX;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		if (query->last_lineno < query->first_lineno) {
37162306a36Sopenharmony_ci			pr_err("last-line:%d < 1st-line:%d\n",
37262306a36Sopenharmony_ci			       query->last_lineno,
37362306a36Sopenharmony_ci			       query->first_lineno);
37462306a36Sopenharmony_ci			return -EINVAL;
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci	} else {
37762306a36Sopenharmony_ci		query->last_lineno = query->first_lineno;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci	v3pr_info("parsed line %d-%d\n", query->first_lineno,
38062306a36Sopenharmony_ci		 query->last_lineno);
38162306a36Sopenharmony_ci	return 0;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int check_set(const char **dest, char *src, char *name)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	int rc = 0;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (*dest) {
38962306a36Sopenharmony_ci		rc = -EINVAL;
39062306a36Sopenharmony_ci		pr_err("match-spec:%s val:%s overridden by %s\n",
39162306a36Sopenharmony_ci		       name, *dest, src);
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci	*dest = src;
39462306a36Sopenharmony_ci	return rc;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/*
39862306a36Sopenharmony_ci * Parse words[] as a ddebug query specification, which is a series
39962306a36Sopenharmony_ci * of (keyword, value) pairs chosen from these possibilities:
40062306a36Sopenharmony_ci *
40162306a36Sopenharmony_ci * func <function-name>
40262306a36Sopenharmony_ci * file <full-pathname>
40362306a36Sopenharmony_ci * file <base-filename>
40462306a36Sopenharmony_ci * module <module-name>
40562306a36Sopenharmony_ci * format <escaped-string-to-find-in-format>
40662306a36Sopenharmony_ci * line <lineno>
40762306a36Sopenharmony_ci * line <first-lineno>-<last-lineno> // where either may be empty
40862306a36Sopenharmony_ci *
40962306a36Sopenharmony_ci * Only 1 of each type is allowed.
41062306a36Sopenharmony_ci * Returns 0 on success, <0 on error.
41162306a36Sopenharmony_ci */
41262306a36Sopenharmony_cistatic int ddebug_parse_query(char *words[], int nwords,
41362306a36Sopenharmony_ci			struct ddebug_query *query, const char *modname)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	unsigned int i;
41662306a36Sopenharmony_ci	int rc = 0;
41762306a36Sopenharmony_ci	char *fline;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* check we have an even number of words */
42062306a36Sopenharmony_ci	if (nwords % 2 != 0) {
42162306a36Sopenharmony_ci		pr_err("expecting pairs of match-spec <value>\n");
42262306a36Sopenharmony_ci		return -EINVAL;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	for (i = 0; i < nwords; i += 2) {
42662306a36Sopenharmony_ci		char *keyword = words[i];
42762306a36Sopenharmony_ci		char *arg = words[i+1];
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		if (!strcmp(keyword, "func")) {
43062306a36Sopenharmony_ci			rc = check_set(&query->function, arg, "func");
43162306a36Sopenharmony_ci		} else if (!strcmp(keyword, "file")) {
43262306a36Sopenharmony_ci			if (check_set(&query->filename, arg, "file"))
43362306a36Sopenharmony_ci				return -EINVAL;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci			/* tail :$info is function or line-range */
43662306a36Sopenharmony_ci			fline = strchr(query->filename, ':');
43762306a36Sopenharmony_ci			if (!fline)
43862306a36Sopenharmony_ci				continue;
43962306a36Sopenharmony_ci			*fline++ = '\0';
44062306a36Sopenharmony_ci			if (isalpha(*fline) || *fline == '*' || *fline == '?') {
44162306a36Sopenharmony_ci				/* take as function name */
44262306a36Sopenharmony_ci				if (check_set(&query->function, fline, "func"))
44362306a36Sopenharmony_ci					return -EINVAL;
44462306a36Sopenharmony_ci			} else {
44562306a36Sopenharmony_ci				if (parse_linerange(query, fline))
44662306a36Sopenharmony_ci					return -EINVAL;
44762306a36Sopenharmony_ci			}
44862306a36Sopenharmony_ci		} else if (!strcmp(keyword, "module")) {
44962306a36Sopenharmony_ci			rc = check_set(&query->module, arg, "module");
45062306a36Sopenharmony_ci		} else if (!strcmp(keyword, "format")) {
45162306a36Sopenharmony_ci			string_unescape_inplace(arg, UNESCAPE_SPACE |
45262306a36Sopenharmony_ci							    UNESCAPE_OCTAL |
45362306a36Sopenharmony_ci							    UNESCAPE_SPECIAL);
45462306a36Sopenharmony_ci			rc = check_set(&query->format, arg, "format");
45562306a36Sopenharmony_ci		} else if (!strcmp(keyword, "line")) {
45662306a36Sopenharmony_ci			if (parse_linerange(query, arg))
45762306a36Sopenharmony_ci				return -EINVAL;
45862306a36Sopenharmony_ci		} else if (!strcmp(keyword, "class")) {
45962306a36Sopenharmony_ci			rc = check_set(&query->class_string, arg, "class");
46062306a36Sopenharmony_ci		} else {
46162306a36Sopenharmony_ci			pr_err("unknown keyword \"%s\"\n", keyword);
46262306a36Sopenharmony_ci			return -EINVAL;
46362306a36Sopenharmony_ci		}
46462306a36Sopenharmony_ci		if (rc)
46562306a36Sopenharmony_ci			return rc;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci	if (!query->module && modname)
46862306a36Sopenharmony_ci		/*
46962306a36Sopenharmony_ci		 * support $modname.dyndbg=<multiple queries>, when
47062306a36Sopenharmony_ci		 * not given in the query itself
47162306a36Sopenharmony_ci		 */
47262306a36Sopenharmony_ci		query->module = modname;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	vpr_info_dq(query, "parsed");
47562306a36Sopenharmony_ci	return 0;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci/*
47962306a36Sopenharmony_ci * Parse `str' as a flags specification, format [-+=][p]+.
48062306a36Sopenharmony_ci * Sets up *maskp and *flagsp to be used when changing the
48162306a36Sopenharmony_ci * flags fields of matched _ddebug's.  Returns 0 on success
48262306a36Sopenharmony_ci * or <0 on error.
48362306a36Sopenharmony_ci */
48462306a36Sopenharmony_cistatic int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	int op, i;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	switch (*str) {
48962306a36Sopenharmony_ci	case '+':
49062306a36Sopenharmony_ci	case '-':
49162306a36Sopenharmony_ci	case '=':
49262306a36Sopenharmony_ci		op = *str++;
49362306a36Sopenharmony_ci		break;
49462306a36Sopenharmony_ci	default:
49562306a36Sopenharmony_ci		pr_err("bad flag-op %c, at start of %s\n", *str, str);
49662306a36Sopenharmony_ci		return -EINVAL;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci	v3pr_info("op='%c'\n", op);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	for (; *str ; ++str) {
50162306a36Sopenharmony_ci		for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) {
50262306a36Sopenharmony_ci			if (*str == opt_array[i].opt_char) {
50362306a36Sopenharmony_ci				modifiers->flags |= opt_array[i].flag;
50462306a36Sopenharmony_ci				break;
50562306a36Sopenharmony_ci			}
50662306a36Sopenharmony_ci		}
50762306a36Sopenharmony_ci		if (i < 0) {
50862306a36Sopenharmony_ci			pr_err("unknown flag '%c'\n", *str);
50962306a36Sopenharmony_ci			return -EINVAL;
51062306a36Sopenharmony_ci		}
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci	v3pr_info("flags=0x%x\n", modifiers->flags);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* calculate final flags, mask based upon op */
51562306a36Sopenharmony_ci	switch (op) {
51662306a36Sopenharmony_ci	case '=':
51762306a36Sopenharmony_ci		/* modifiers->flags already set */
51862306a36Sopenharmony_ci		modifiers->mask = 0;
51962306a36Sopenharmony_ci		break;
52062306a36Sopenharmony_ci	case '+':
52162306a36Sopenharmony_ci		modifiers->mask = ~0U;
52262306a36Sopenharmony_ci		break;
52362306a36Sopenharmony_ci	case '-':
52462306a36Sopenharmony_ci		modifiers->mask = ~modifiers->flags;
52562306a36Sopenharmony_ci		modifiers->flags = 0;
52662306a36Sopenharmony_ci		break;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci	v3pr_info("*flagsp=0x%x *maskp=0x%x\n", modifiers->flags, modifiers->mask);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return 0;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic int ddebug_exec_query(char *query_string, const char *modname)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	struct flag_settings modifiers = {};
53662306a36Sopenharmony_ci	struct ddebug_query query = {};
53762306a36Sopenharmony_ci#define MAXWORDS 9
53862306a36Sopenharmony_ci	int nwords, nfound;
53962306a36Sopenharmony_ci	char *words[MAXWORDS];
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	nwords = ddebug_tokenize(query_string, words, MAXWORDS);
54262306a36Sopenharmony_ci	if (nwords <= 0) {
54362306a36Sopenharmony_ci		pr_err("tokenize failed\n");
54462306a36Sopenharmony_ci		return -EINVAL;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci	/* check flags 1st (last arg) so query is pairs of spec,val */
54762306a36Sopenharmony_ci	if (ddebug_parse_flags(words[nwords-1], &modifiers)) {
54862306a36Sopenharmony_ci		pr_err("flags parse failed\n");
54962306a36Sopenharmony_ci		return -EINVAL;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci	if (ddebug_parse_query(words, nwords-1, &query, modname)) {
55262306a36Sopenharmony_ci		pr_err("query parse failed\n");
55362306a36Sopenharmony_ci		return -EINVAL;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci	/* actually go and implement the change */
55662306a36Sopenharmony_ci	nfound = ddebug_change(&query, &modifiers);
55762306a36Sopenharmony_ci	vpr_info_dq(&query, nfound ? "applied" : "no-match");
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return nfound;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci/* handle multiple queries in query string, continue on error, return
56362306a36Sopenharmony_ci   last error or number of matching callsites.  Module name is either
56462306a36Sopenharmony_ci   in param (for boot arg) or perhaps in query string.
56562306a36Sopenharmony_ci*/
56662306a36Sopenharmony_cistatic int ddebug_exec_queries(char *query, const char *modname)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	char *split;
56962306a36Sopenharmony_ci	int i, errs = 0, exitcode = 0, rc, nfound = 0;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	for (i = 0; query; query = split) {
57262306a36Sopenharmony_ci		split = strpbrk(query, ";\n");
57362306a36Sopenharmony_ci		if (split)
57462306a36Sopenharmony_ci			*split++ = '\0';
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		query = skip_spaces(query);
57762306a36Sopenharmony_ci		if (!query || !*query || *query == '#')
57862306a36Sopenharmony_ci			continue;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		vpr_info("query %d: \"%s\" mod:%s\n", i, query, modname ?: "*");
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci		rc = ddebug_exec_query(query, modname);
58362306a36Sopenharmony_ci		if (rc < 0) {
58462306a36Sopenharmony_ci			errs++;
58562306a36Sopenharmony_ci			exitcode = rc;
58662306a36Sopenharmony_ci		} else {
58762306a36Sopenharmony_ci			nfound += rc;
58862306a36Sopenharmony_ci		}
58962306a36Sopenharmony_ci		i++;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci	if (i)
59262306a36Sopenharmony_ci		v2pr_info("processed %d queries, with %d matches, %d errs\n",
59362306a36Sopenharmony_ci			 i, nfound, errs);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (exitcode)
59662306a36Sopenharmony_ci		return exitcode;
59762306a36Sopenharmony_ci	return nfound;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci/* apply a new bitmap to the sys-knob's current bit-state */
60162306a36Sopenharmony_cistatic int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
60262306a36Sopenharmony_ci				     unsigned long *new_bits, unsigned long *old_bits)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci#define QUERY_SIZE 128
60562306a36Sopenharmony_ci	char query[QUERY_SIZE];
60662306a36Sopenharmony_ci	const struct ddebug_class_map *map = dcp->map;
60762306a36Sopenharmony_ci	int matches = 0;
60862306a36Sopenharmony_ci	int bi, ct;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	v2pr_info("apply: 0x%lx to: 0x%lx\n", *new_bits, *old_bits);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	for (bi = 0; bi < map->length; bi++) {
61362306a36Sopenharmony_ci		if (test_bit(bi, new_bits) == test_bit(bi, old_bits))
61462306a36Sopenharmony_ci			continue;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci		snprintf(query, QUERY_SIZE, "class %s %c%s", map->class_names[bi],
61762306a36Sopenharmony_ci			 test_bit(bi, new_bits) ? '+' : '-', dcp->flags);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		ct = ddebug_exec_queries(query, NULL);
62062306a36Sopenharmony_ci		matches += ct;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci		v2pr_info("bit_%d: %d matches on class: %s -> 0x%lx\n", bi,
62362306a36Sopenharmony_ci			  ct, map->class_names[bi], *new_bits);
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci	return matches;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci/* stub to later conditionally add "$module." prefix where not already done */
62962306a36Sopenharmony_ci#define KP_NAME(kp)	kp->name
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci#define CLASSMAP_BITMASK(width) ((1UL << (width)) - 1)
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci/* accept comma-separated-list of [+-] classnames */
63462306a36Sopenharmony_cistatic int param_set_dyndbg_classnames(const char *instr, const struct kernel_param *kp)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	const struct ddebug_class_param *dcp = kp->arg;
63762306a36Sopenharmony_ci	const struct ddebug_class_map *map = dcp->map;
63862306a36Sopenharmony_ci	unsigned long curr_bits, old_bits;
63962306a36Sopenharmony_ci	char *cl_str, *p, *tmp;
64062306a36Sopenharmony_ci	int cls_id, totct = 0;
64162306a36Sopenharmony_ci	bool wanted;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	cl_str = tmp = kstrdup(instr, GFP_KERNEL);
64462306a36Sopenharmony_ci	p = strchr(cl_str, '\n');
64562306a36Sopenharmony_ci	if (p)
64662306a36Sopenharmony_ci		*p = '\0';
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	/* start with previously set state-bits, then modify */
64962306a36Sopenharmony_ci	curr_bits = old_bits = *dcp->bits;
65062306a36Sopenharmony_ci	vpr_info("\"%s\" > %s:0x%lx\n", cl_str, KP_NAME(kp), curr_bits);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	for (; cl_str; cl_str = p) {
65362306a36Sopenharmony_ci		p = strchr(cl_str, ',');
65462306a36Sopenharmony_ci		if (p)
65562306a36Sopenharmony_ci			*p++ = '\0';
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		if (*cl_str == '-') {
65862306a36Sopenharmony_ci			wanted = false;
65962306a36Sopenharmony_ci			cl_str++;
66062306a36Sopenharmony_ci		} else {
66162306a36Sopenharmony_ci			wanted = true;
66262306a36Sopenharmony_ci			if (*cl_str == '+')
66362306a36Sopenharmony_ci				cl_str++;
66462306a36Sopenharmony_ci		}
66562306a36Sopenharmony_ci		cls_id = match_string(map->class_names, map->length, cl_str);
66662306a36Sopenharmony_ci		if (cls_id < 0) {
66762306a36Sopenharmony_ci			pr_err("%s unknown to %s\n", cl_str, KP_NAME(kp));
66862306a36Sopenharmony_ci			continue;
66962306a36Sopenharmony_ci		}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		/* have one or more valid class_ids of one *_NAMES type */
67262306a36Sopenharmony_ci		switch (map->map_type) {
67362306a36Sopenharmony_ci		case DD_CLASS_TYPE_DISJOINT_NAMES:
67462306a36Sopenharmony_ci			/* the +/- pertains to a single bit */
67562306a36Sopenharmony_ci			if (test_bit(cls_id, &curr_bits) == wanted) {
67662306a36Sopenharmony_ci				v3pr_info("no change on %s\n", cl_str);
67762306a36Sopenharmony_ci				continue;
67862306a36Sopenharmony_ci			}
67962306a36Sopenharmony_ci			curr_bits ^= BIT(cls_id);
68062306a36Sopenharmony_ci			totct += ddebug_apply_class_bitmap(dcp, &curr_bits, dcp->bits);
68162306a36Sopenharmony_ci			*dcp->bits = curr_bits;
68262306a36Sopenharmony_ci			v2pr_info("%s: changed bit %d:%s\n", KP_NAME(kp), cls_id,
68362306a36Sopenharmony_ci				  map->class_names[cls_id]);
68462306a36Sopenharmony_ci			break;
68562306a36Sopenharmony_ci		case DD_CLASS_TYPE_LEVEL_NAMES:
68662306a36Sopenharmony_ci			/* cls_id = N in 0..max. wanted +/- determines N or N-1 */
68762306a36Sopenharmony_ci			old_bits = CLASSMAP_BITMASK(*dcp->lvl);
68862306a36Sopenharmony_ci			curr_bits = CLASSMAP_BITMASK(cls_id + (wanted ? 1 : 0 ));
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci			totct += ddebug_apply_class_bitmap(dcp, &curr_bits, &old_bits);
69162306a36Sopenharmony_ci			*dcp->lvl = (cls_id + (wanted ? 1 : 0));
69262306a36Sopenharmony_ci			v2pr_info("%s: changed bit-%d: \"%s\" %lx->%lx\n", KP_NAME(kp), cls_id,
69362306a36Sopenharmony_ci				  map->class_names[cls_id], old_bits, curr_bits);
69462306a36Sopenharmony_ci			break;
69562306a36Sopenharmony_ci		default:
69662306a36Sopenharmony_ci			pr_err("illegal map-type value %d\n", map->map_type);
69762306a36Sopenharmony_ci		}
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci	kfree(tmp);
70062306a36Sopenharmony_ci	vpr_info("total matches: %d\n", totct);
70162306a36Sopenharmony_ci	return 0;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci/**
70562306a36Sopenharmony_ci * param_set_dyndbg_classes - class FOO >control
70662306a36Sopenharmony_ci * @instr: string echo>d to sysfs, input depends on map_type
70762306a36Sopenharmony_ci * @kp:    kp->arg has state: bits/lvl, map, map_type
70862306a36Sopenharmony_ci *
70962306a36Sopenharmony_ci * Enable/disable prdbgs by their class, as given in the arguments to
71062306a36Sopenharmony_ci * DECLARE_DYNDBG_CLASSMAP.  For LEVEL map-types, enforce relative
71162306a36Sopenharmony_ci * levels by bitpos.
71262306a36Sopenharmony_ci *
71362306a36Sopenharmony_ci * Returns: 0 or <0 if error.
71462306a36Sopenharmony_ci */
71562306a36Sopenharmony_ciint param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	const struct ddebug_class_param *dcp = kp->arg;
71862306a36Sopenharmony_ci	const struct ddebug_class_map *map = dcp->map;
71962306a36Sopenharmony_ci	unsigned long inrep, new_bits, old_bits;
72062306a36Sopenharmony_ci	int rc, totct = 0;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	switch (map->map_type) {
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	case DD_CLASS_TYPE_DISJOINT_NAMES:
72562306a36Sopenharmony_ci	case DD_CLASS_TYPE_LEVEL_NAMES:
72662306a36Sopenharmony_ci		/* handle [+-]classnames list separately, we are done here */
72762306a36Sopenharmony_ci		return param_set_dyndbg_classnames(instr, kp);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	case DD_CLASS_TYPE_DISJOINT_BITS:
73062306a36Sopenharmony_ci	case DD_CLASS_TYPE_LEVEL_NUM:
73162306a36Sopenharmony_ci		/* numeric input, accept and fall-thru */
73262306a36Sopenharmony_ci		rc = kstrtoul(instr, 0, &inrep);
73362306a36Sopenharmony_ci		if (rc) {
73462306a36Sopenharmony_ci			pr_err("expecting numeric input: %s > %s\n", instr, KP_NAME(kp));
73562306a36Sopenharmony_ci			return -EINVAL;
73662306a36Sopenharmony_ci		}
73762306a36Sopenharmony_ci		break;
73862306a36Sopenharmony_ci	default:
73962306a36Sopenharmony_ci		pr_err("%s: bad map type: %d\n", KP_NAME(kp), map->map_type);
74062306a36Sopenharmony_ci		return -EINVAL;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	/* only _BITS,_NUM (numeric) map-types get here */
74462306a36Sopenharmony_ci	switch (map->map_type) {
74562306a36Sopenharmony_ci	case DD_CLASS_TYPE_DISJOINT_BITS:
74662306a36Sopenharmony_ci		/* expect bits. mask and warn if too many */
74762306a36Sopenharmony_ci		if (inrep & ~CLASSMAP_BITMASK(map->length)) {
74862306a36Sopenharmony_ci			pr_warn("%s: input: 0x%lx exceeds mask: 0x%lx, masking\n",
74962306a36Sopenharmony_ci				KP_NAME(kp), inrep, CLASSMAP_BITMASK(map->length));
75062306a36Sopenharmony_ci			inrep &= CLASSMAP_BITMASK(map->length);
75162306a36Sopenharmony_ci		}
75262306a36Sopenharmony_ci		v2pr_info("bits:%lx > %s\n", inrep, KP_NAME(kp));
75362306a36Sopenharmony_ci		totct += ddebug_apply_class_bitmap(dcp, &inrep, dcp->bits);
75462306a36Sopenharmony_ci		*dcp->bits = inrep;
75562306a36Sopenharmony_ci		break;
75662306a36Sopenharmony_ci	case DD_CLASS_TYPE_LEVEL_NUM:
75762306a36Sopenharmony_ci		/* input is bitpos, of highest verbosity to be enabled */
75862306a36Sopenharmony_ci		if (inrep > map->length) {
75962306a36Sopenharmony_ci			pr_warn("%s: level:%ld exceeds max:%d, clamping\n",
76062306a36Sopenharmony_ci				KP_NAME(kp), inrep, map->length);
76162306a36Sopenharmony_ci			inrep = map->length;
76262306a36Sopenharmony_ci		}
76362306a36Sopenharmony_ci		old_bits = CLASSMAP_BITMASK(*dcp->lvl);
76462306a36Sopenharmony_ci		new_bits = CLASSMAP_BITMASK(inrep);
76562306a36Sopenharmony_ci		v2pr_info("lvl:%ld bits:0x%lx > %s\n", inrep, new_bits, KP_NAME(kp));
76662306a36Sopenharmony_ci		totct += ddebug_apply_class_bitmap(dcp, &new_bits, &old_bits);
76762306a36Sopenharmony_ci		*dcp->lvl = inrep;
76862306a36Sopenharmony_ci		break;
76962306a36Sopenharmony_ci	default:
77062306a36Sopenharmony_ci		pr_warn("%s: bad map type: %d\n", KP_NAME(kp), map->map_type);
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci	vpr_info("%s: total matches: %d\n", KP_NAME(kp), totct);
77362306a36Sopenharmony_ci	return 0;
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ciEXPORT_SYMBOL(param_set_dyndbg_classes);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci/**
77862306a36Sopenharmony_ci * param_get_dyndbg_classes - classes reader
77962306a36Sopenharmony_ci * @buffer: string description of controlled bits -> classes
78062306a36Sopenharmony_ci * @kp:     kp->arg has state: bits, map
78162306a36Sopenharmony_ci *
78262306a36Sopenharmony_ci * Reads last written state, underlying prdbg state may have been
78362306a36Sopenharmony_ci * altered by direct >control.  Displays 0x for DISJOINT, 0-N for
78462306a36Sopenharmony_ci * LEVEL Returns: #chars written or <0 on error
78562306a36Sopenharmony_ci */
78662306a36Sopenharmony_ciint param_get_dyndbg_classes(char *buffer, const struct kernel_param *kp)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	const struct ddebug_class_param *dcp = kp->arg;
78962306a36Sopenharmony_ci	const struct ddebug_class_map *map = dcp->map;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	switch (map->map_type) {
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	case DD_CLASS_TYPE_DISJOINT_NAMES:
79462306a36Sopenharmony_ci	case DD_CLASS_TYPE_DISJOINT_BITS:
79562306a36Sopenharmony_ci		return scnprintf(buffer, PAGE_SIZE, "0x%lx\n", *dcp->bits);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	case DD_CLASS_TYPE_LEVEL_NAMES:
79862306a36Sopenharmony_ci	case DD_CLASS_TYPE_LEVEL_NUM:
79962306a36Sopenharmony_ci		return scnprintf(buffer, PAGE_SIZE, "%d\n", *dcp->lvl);
80062306a36Sopenharmony_ci	default:
80162306a36Sopenharmony_ci		return -1;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ciEXPORT_SYMBOL(param_get_dyndbg_classes);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ciconst struct kernel_param_ops param_ops_dyndbg_classes = {
80762306a36Sopenharmony_ci	.set = param_set_dyndbg_classes,
80862306a36Sopenharmony_ci	.get = param_get_dyndbg_classes,
80962306a36Sopenharmony_ci};
81062306a36Sopenharmony_ciEXPORT_SYMBOL(param_ops_dyndbg_classes);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci#define PREFIX_SIZE 128
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic int remaining(int wrote)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	if (PREFIX_SIZE - wrote > 0)
81762306a36Sopenharmony_ci		return PREFIX_SIZE - wrote;
81862306a36Sopenharmony_ci	return 0;
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic char *__dynamic_emit_prefix(const struct _ddebug *desc, char *buf)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	int pos_after_tid;
82462306a36Sopenharmony_ci	int pos = 0;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (desc->flags & _DPRINTK_FLAGS_INCL_TID) {
82762306a36Sopenharmony_ci		if (in_interrupt())
82862306a36Sopenharmony_ci			pos += snprintf(buf + pos, remaining(pos), "<intr> ");
82962306a36Sopenharmony_ci		else
83062306a36Sopenharmony_ci			pos += snprintf(buf + pos, remaining(pos), "[%d] ",
83162306a36Sopenharmony_ci					task_pid_vnr(current));
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci	pos_after_tid = pos;
83462306a36Sopenharmony_ci	if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME)
83562306a36Sopenharmony_ci		pos += snprintf(buf + pos, remaining(pos), "%s:",
83662306a36Sopenharmony_ci				desc->modname);
83762306a36Sopenharmony_ci	if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME)
83862306a36Sopenharmony_ci		pos += snprintf(buf + pos, remaining(pos), "%s:",
83962306a36Sopenharmony_ci				desc->function);
84062306a36Sopenharmony_ci	if (desc->flags & _DPRINTK_FLAGS_INCL_SOURCENAME)
84162306a36Sopenharmony_ci		pos += snprintf(buf + pos, remaining(pos), "%s:",
84262306a36Sopenharmony_ci				trim_prefix(desc->filename));
84362306a36Sopenharmony_ci	if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO)
84462306a36Sopenharmony_ci		pos += snprintf(buf + pos, remaining(pos), "%d:",
84562306a36Sopenharmony_ci				desc->lineno);
84662306a36Sopenharmony_ci	if (pos - pos_after_tid)
84762306a36Sopenharmony_ci		pos += snprintf(buf + pos, remaining(pos), " ");
84862306a36Sopenharmony_ci	if (pos >= PREFIX_SIZE)
84962306a36Sopenharmony_ci		buf[PREFIX_SIZE - 1] = '\0';
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return buf;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_cistatic inline char *dynamic_emit_prefix(struct _ddebug *desc, char *buf)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	if (unlikely(desc->flags & _DPRINTK_FLAGS_INCL_ANY))
85762306a36Sopenharmony_ci		return __dynamic_emit_prefix(desc, buf);
85862306a36Sopenharmony_ci	return buf;
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_civoid __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	va_list args;
86462306a36Sopenharmony_ci	struct va_format vaf;
86562306a36Sopenharmony_ci	char buf[PREFIX_SIZE] = "";
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	BUG_ON(!descriptor);
86862306a36Sopenharmony_ci	BUG_ON(!fmt);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	va_start(args, fmt);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	vaf.fmt = fmt;
87362306a36Sopenharmony_ci	vaf.va = &args;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	printk(KERN_DEBUG "%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	va_end(args);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ciEXPORT_SYMBOL(__dynamic_pr_debug);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_civoid __dynamic_dev_dbg(struct _ddebug *descriptor,
88262306a36Sopenharmony_ci		      const struct device *dev, const char *fmt, ...)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct va_format vaf;
88562306a36Sopenharmony_ci	va_list args;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	BUG_ON(!descriptor);
88862306a36Sopenharmony_ci	BUG_ON(!fmt);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	va_start(args, fmt);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	vaf.fmt = fmt;
89362306a36Sopenharmony_ci	vaf.va = &args;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (!dev) {
89662306a36Sopenharmony_ci		printk(KERN_DEBUG "(NULL device *): %pV", &vaf);
89762306a36Sopenharmony_ci	} else {
89862306a36Sopenharmony_ci		char buf[PREFIX_SIZE] = "";
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci		dev_printk_emit(LOGLEVEL_DEBUG, dev, "%s%s %s: %pV",
90162306a36Sopenharmony_ci				dynamic_emit_prefix(descriptor, buf),
90262306a36Sopenharmony_ci				dev_driver_string(dev), dev_name(dev),
90362306a36Sopenharmony_ci				&vaf);
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	va_end(args);
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ciEXPORT_SYMBOL(__dynamic_dev_dbg);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci#ifdef CONFIG_NET
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_civoid __dynamic_netdev_dbg(struct _ddebug *descriptor,
91362306a36Sopenharmony_ci			  const struct net_device *dev, const char *fmt, ...)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct va_format vaf;
91662306a36Sopenharmony_ci	va_list args;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	BUG_ON(!descriptor);
91962306a36Sopenharmony_ci	BUG_ON(!fmt);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	va_start(args, fmt);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	vaf.fmt = fmt;
92462306a36Sopenharmony_ci	vaf.va = &args;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	if (dev && dev->dev.parent) {
92762306a36Sopenharmony_ci		char buf[PREFIX_SIZE] = "";
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci		dev_printk_emit(LOGLEVEL_DEBUG, dev->dev.parent,
93062306a36Sopenharmony_ci				"%s%s %s %s%s: %pV",
93162306a36Sopenharmony_ci				dynamic_emit_prefix(descriptor, buf),
93262306a36Sopenharmony_ci				dev_driver_string(dev->dev.parent),
93362306a36Sopenharmony_ci				dev_name(dev->dev.parent),
93462306a36Sopenharmony_ci				netdev_name(dev), netdev_reg_state(dev),
93562306a36Sopenharmony_ci				&vaf);
93662306a36Sopenharmony_ci	} else if (dev) {
93762306a36Sopenharmony_ci		printk(KERN_DEBUG "%s%s: %pV", netdev_name(dev),
93862306a36Sopenharmony_ci		       netdev_reg_state(dev), &vaf);
93962306a36Sopenharmony_ci	} else {
94062306a36Sopenharmony_ci		printk(KERN_DEBUG "(NULL net_device): %pV", &vaf);
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	va_end(args);
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ciEXPORT_SYMBOL(__dynamic_netdev_dbg);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci#endif
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INFINIBAND)
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_civoid __dynamic_ibdev_dbg(struct _ddebug *descriptor,
95262306a36Sopenharmony_ci			 const struct ib_device *ibdev, const char *fmt, ...)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	struct va_format vaf;
95562306a36Sopenharmony_ci	va_list args;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	va_start(args, fmt);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	vaf.fmt = fmt;
96062306a36Sopenharmony_ci	vaf.va = &args;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (ibdev && ibdev->dev.parent) {
96362306a36Sopenharmony_ci		char buf[PREFIX_SIZE] = "";
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci		dev_printk_emit(LOGLEVEL_DEBUG, ibdev->dev.parent,
96662306a36Sopenharmony_ci				"%s%s %s %s: %pV",
96762306a36Sopenharmony_ci				dynamic_emit_prefix(descriptor, buf),
96862306a36Sopenharmony_ci				dev_driver_string(ibdev->dev.parent),
96962306a36Sopenharmony_ci				dev_name(ibdev->dev.parent),
97062306a36Sopenharmony_ci				dev_name(&ibdev->dev),
97162306a36Sopenharmony_ci				&vaf);
97262306a36Sopenharmony_ci	} else if (ibdev) {
97362306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: %pV", dev_name(&ibdev->dev), &vaf);
97462306a36Sopenharmony_ci	} else {
97562306a36Sopenharmony_ci		printk(KERN_DEBUG "(NULL ib_device): %pV", &vaf);
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	va_end(args);
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ciEXPORT_SYMBOL(__dynamic_ibdev_dbg);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci#endif
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci/*
98562306a36Sopenharmony_ci * Install a noop handler to make dyndbg look like a normal kernel cli param.
98662306a36Sopenharmony_ci * This avoids warnings about dyndbg being an unknown cli param when supplied
98762306a36Sopenharmony_ci * by a user.
98862306a36Sopenharmony_ci */
98962306a36Sopenharmony_cistatic __init int dyndbg_setup(char *str)
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	return 1;
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci__setup("dyndbg=", dyndbg_setup);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci/*
99762306a36Sopenharmony_ci * File_ops->write method for <debugfs>/dynamic_debug/control.  Gathers the
99862306a36Sopenharmony_ci * command text from userspace, parses and executes it.
99962306a36Sopenharmony_ci */
100062306a36Sopenharmony_ci#define USER_BUF_PAGE 4096
100162306a36Sopenharmony_cistatic ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf,
100262306a36Sopenharmony_ci				  size_t len, loff_t *offp)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	char *tmpbuf;
100562306a36Sopenharmony_ci	int ret;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (len == 0)
100862306a36Sopenharmony_ci		return 0;
100962306a36Sopenharmony_ci	if (len > USER_BUF_PAGE - 1) {
101062306a36Sopenharmony_ci		pr_warn("expected <%d bytes into control\n", USER_BUF_PAGE);
101162306a36Sopenharmony_ci		return -E2BIG;
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci	tmpbuf = memdup_user_nul(ubuf, len);
101462306a36Sopenharmony_ci	if (IS_ERR(tmpbuf))
101562306a36Sopenharmony_ci		return PTR_ERR(tmpbuf);
101662306a36Sopenharmony_ci	v2pr_info("read %zu bytes from userspace\n", len);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	ret = ddebug_exec_queries(tmpbuf, NULL);
101962306a36Sopenharmony_ci	kfree(tmpbuf);
102062306a36Sopenharmony_ci	if (ret < 0)
102162306a36Sopenharmony_ci		return ret;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	*offp += len;
102462306a36Sopenharmony_ci	return len;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci/*
102862306a36Sopenharmony_ci * Set the iterator to point to the first _ddebug object
102962306a36Sopenharmony_ci * and return a pointer to that first object.  Returns
103062306a36Sopenharmony_ci * NULL if there are no _ddebugs at all.
103162306a36Sopenharmony_ci */
103262306a36Sopenharmony_cistatic struct _ddebug *ddebug_iter_first(struct ddebug_iter *iter)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	if (list_empty(&ddebug_tables)) {
103562306a36Sopenharmony_ci		iter->table = NULL;
103662306a36Sopenharmony_ci		return NULL;
103762306a36Sopenharmony_ci	}
103862306a36Sopenharmony_ci	iter->table = list_entry(ddebug_tables.next,
103962306a36Sopenharmony_ci				 struct ddebug_table, link);
104062306a36Sopenharmony_ci	iter->idx = iter->table->num_ddebugs;
104162306a36Sopenharmony_ci	return &iter->table->ddebugs[--iter->idx];
104262306a36Sopenharmony_ci}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci/*
104562306a36Sopenharmony_ci * Advance the iterator to point to the next _ddebug
104662306a36Sopenharmony_ci * object from the one the iterator currently points at,
104762306a36Sopenharmony_ci * and returns a pointer to the new _ddebug.  Returns
104862306a36Sopenharmony_ci * NULL if the iterator has seen all the _ddebugs.
104962306a36Sopenharmony_ci */
105062306a36Sopenharmony_cistatic struct _ddebug *ddebug_iter_next(struct ddebug_iter *iter)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci	if (iter->table == NULL)
105362306a36Sopenharmony_ci		return NULL;
105462306a36Sopenharmony_ci	if (--iter->idx < 0) {
105562306a36Sopenharmony_ci		/* iterate to next table */
105662306a36Sopenharmony_ci		if (list_is_last(&iter->table->link, &ddebug_tables)) {
105762306a36Sopenharmony_ci			iter->table = NULL;
105862306a36Sopenharmony_ci			return NULL;
105962306a36Sopenharmony_ci		}
106062306a36Sopenharmony_ci		iter->table = list_entry(iter->table->link.next,
106162306a36Sopenharmony_ci					 struct ddebug_table, link);
106262306a36Sopenharmony_ci		iter->idx = iter->table->num_ddebugs;
106362306a36Sopenharmony_ci		--iter->idx;
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci	return &iter->table->ddebugs[iter->idx];
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci/*
106962306a36Sopenharmony_ci * Seq_ops start method.  Called at the start of every
107062306a36Sopenharmony_ci * read() call from userspace.  Takes the ddebug_lock and
107162306a36Sopenharmony_ci * seeks the seq_file's iterator to the given position.
107262306a36Sopenharmony_ci */
107362306a36Sopenharmony_cistatic void *ddebug_proc_start(struct seq_file *m, loff_t *pos)
107462306a36Sopenharmony_ci{
107562306a36Sopenharmony_ci	struct ddebug_iter *iter = m->private;
107662306a36Sopenharmony_ci	struct _ddebug *dp;
107762306a36Sopenharmony_ci	int n = *pos;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	mutex_lock(&ddebug_lock);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (!n)
108262306a36Sopenharmony_ci		return SEQ_START_TOKEN;
108362306a36Sopenharmony_ci	if (n < 0)
108462306a36Sopenharmony_ci		return NULL;
108562306a36Sopenharmony_ci	dp = ddebug_iter_first(iter);
108662306a36Sopenharmony_ci	while (dp != NULL && --n > 0)
108762306a36Sopenharmony_ci		dp = ddebug_iter_next(iter);
108862306a36Sopenharmony_ci	return dp;
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci/*
109262306a36Sopenharmony_ci * Seq_ops next method.  Called several times within a read()
109362306a36Sopenharmony_ci * call from userspace, with ddebug_lock held.  Walks to the
109462306a36Sopenharmony_ci * next _ddebug object with a special case for the header line.
109562306a36Sopenharmony_ci */
109662306a36Sopenharmony_cistatic void *ddebug_proc_next(struct seq_file *m, void *p, loff_t *pos)
109762306a36Sopenharmony_ci{
109862306a36Sopenharmony_ci	struct ddebug_iter *iter = m->private;
109962306a36Sopenharmony_ci	struct _ddebug *dp;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	if (p == SEQ_START_TOKEN)
110262306a36Sopenharmony_ci		dp = ddebug_iter_first(iter);
110362306a36Sopenharmony_ci	else
110462306a36Sopenharmony_ci		dp = ddebug_iter_next(iter);
110562306a36Sopenharmony_ci	++*pos;
110662306a36Sopenharmony_ci	return dp;
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci#define class_in_range(class_id, map)					\
111062306a36Sopenharmony_ci	(class_id >= map->base && class_id < map->base + map->length)
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic const char *ddebug_class_name(struct ddebug_iter *iter, struct _ddebug *dp)
111362306a36Sopenharmony_ci{
111462306a36Sopenharmony_ci	struct ddebug_class_map *map;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	list_for_each_entry(map, &iter->table->maps, link)
111762306a36Sopenharmony_ci		if (class_in_range(dp->class_id, map))
111862306a36Sopenharmony_ci			return map->class_names[dp->class_id - map->base];
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	return NULL;
112162306a36Sopenharmony_ci}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci/*
112462306a36Sopenharmony_ci * Seq_ops show method.  Called several times within a read()
112562306a36Sopenharmony_ci * call from userspace, with ddebug_lock held.  Formats the
112662306a36Sopenharmony_ci * current _ddebug as a single human-readable line, with a
112762306a36Sopenharmony_ci * special case for the header line.
112862306a36Sopenharmony_ci */
112962306a36Sopenharmony_cistatic int ddebug_proc_show(struct seq_file *m, void *p)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	struct ddebug_iter *iter = m->private;
113262306a36Sopenharmony_ci	struct _ddebug *dp = p;
113362306a36Sopenharmony_ci	struct flagsbuf flags;
113462306a36Sopenharmony_ci	char const *class;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	if (p == SEQ_START_TOKEN) {
113762306a36Sopenharmony_ci		seq_puts(m,
113862306a36Sopenharmony_ci			 "# filename:lineno [module]function flags format\n");
113962306a36Sopenharmony_ci		return 0;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	seq_printf(m, "%s:%u [%s]%s =%s \"",
114362306a36Sopenharmony_ci		   trim_prefix(dp->filename), dp->lineno,
114462306a36Sopenharmony_ci		   iter->table->mod_name, dp->function,
114562306a36Sopenharmony_ci		   ddebug_describe_flags(dp->flags, &flags));
114662306a36Sopenharmony_ci	seq_escape_str(m, dp->format, ESCAPE_SPACE, "\t\r\n\"");
114762306a36Sopenharmony_ci	seq_puts(m, "\"");
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	if (dp->class_id != _DPRINTK_CLASS_DFLT) {
115062306a36Sopenharmony_ci		class = ddebug_class_name(iter, dp);
115162306a36Sopenharmony_ci		if (class)
115262306a36Sopenharmony_ci			seq_printf(m, " class:%s", class);
115362306a36Sopenharmony_ci		else
115462306a36Sopenharmony_ci			seq_printf(m, " class unknown, _id:%d", dp->class_id);
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci	seq_puts(m, "\n");
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	return 0;
115962306a36Sopenharmony_ci}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci/*
116262306a36Sopenharmony_ci * Seq_ops stop method.  Called at the end of each read()
116362306a36Sopenharmony_ci * call from userspace.  Drops ddebug_lock.
116462306a36Sopenharmony_ci */
116562306a36Sopenharmony_cistatic void ddebug_proc_stop(struct seq_file *m, void *p)
116662306a36Sopenharmony_ci{
116762306a36Sopenharmony_ci	mutex_unlock(&ddebug_lock);
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cistatic const struct seq_operations ddebug_proc_seqops = {
117162306a36Sopenharmony_ci	.start = ddebug_proc_start,
117262306a36Sopenharmony_ci	.next = ddebug_proc_next,
117362306a36Sopenharmony_ci	.show = ddebug_proc_show,
117462306a36Sopenharmony_ci	.stop = ddebug_proc_stop
117562306a36Sopenharmony_ci};
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cistatic int ddebug_proc_open(struct inode *inode, struct file *file)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	return seq_open_private(file, &ddebug_proc_seqops,
118062306a36Sopenharmony_ci				sizeof(struct ddebug_iter));
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic const struct file_operations ddebug_proc_fops = {
118462306a36Sopenharmony_ci	.owner = THIS_MODULE,
118562306a36Sopenharmony_ci	.open = ddebug_proc_open,
118662306a36Sopenharmony_ci	.read = seq_read,
118762306a36Sopenharmony_ci	.llseek = seq_lseek,
118862306a36Sopenharmony_ci	.release = seq_release_private,
118962306a36Sopenharmony_ci	.write = ddebug_proc_write
119062306a36Sopenharmony_ci};
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_cistatic const struct proc_ops proc_fops = {
119362306a36Sopenharmony_ci	.proc_open = ddebug_proc_open,
119462306a36Sopenharmony_ci	.proc_read = seq_read,
119562306a36Sopenharmony_ci	.proc_lseek = seq_lseek,
119662306a36Sopenharmony_ci	.proc_release = seq_release_private,
119762306a36Sopenharmony_ci	.proc_write = ddebug_proc_write
119862306a36Sopenharmony_ci};
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_cistatic void ddebug_attach_module_classes(struct ddebug_table *dt,
120162306a36Sopenharmony_ci					 struct ddebug_class_map *classes,
120262306a36Sopenharmony_ci					 int num_classes)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	struct ddebug_class_map *cm;
120562306a36Sopenharmony_ci	int i, j, ct = 0;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	for (cm = classes, i = 0; i < num_classes; i++, cm++) {
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci		if (!strcmp(cm->mod_name, dt->mod_name)) {
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci			v2pr_info("class[%d]: module:%s base:%d len:%d ty:%d\n", i,
121262306a36Sopenharmony_ci				  cm->mod_name, cm->base, cm->length, cm->map_type);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci			for (j = 0; j < cm->length; j++)
121562306a36Sopenharmony_ci				v3pr_info(" %d: %d %s\n", j + cm->base, j,
121662306a36Sopenharmony_ci					  cm->class_names[j]);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci			list_add(&cm->link, &dt->maps);
121962306a36Sopenharmony_ci			ct++;
122062306a36Sopenharmony_ci		}
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci	if (ct)
122362306a36Sopenharmony_ci		vpr_info("module:%s attached %d classes\n", dt->mod_name, ct);
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci/*
122762306a36Sopenharmony_ci * Allocate a new ddebug_table for the given module
122862306a36Sopenharmony_ci * and add it to the global list.
122962306a36Sopenharmony_ci */
123062306a36Sopenharmony_cistatic int ddebug_add_module(struct _ddebug_info *di, const char *modname)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	struct ddebug_table *dt;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	v3pr_info("add-module: %s.%d sites\n", modname, di->num_descs);
123562306a36Sopenharmony_ci	if (!di->num_descs) {
123662306a36Sopenharmony_ci		v3pr_info(" skip %s\n", modname);
123762306a36Sopenharmony_ci		return 0;
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	dt = kzalloc(sizeof(*dt), GFP_KERNEL);
124162306a36Sopenharmony_ci	if (dt == NULL) {
124262306a36Sopenharmony_ci		pr_err("error adding module: %s\n", modname);
124362306a36Sopenharmony_ci		return -ENOMEM;
124462306a36Sopenharmony_ci	}
124562306a36Sopenharmony_ci	/*
124662306a36Sopenharmony_ci	 * For built-in modules, name lives in .rodata and is
124762306a36Sopenharmony_ci	 * immortal. For loaded modules, name points at the name[]
124862306a36Sopenharmony_ci	 * member of struct module, which lives at least as long as
124962306a36Sopenharmony_ci	 * this struct ddebug_table.
125062306a36Sopenharmony_ci	 */
125162306a36Sopenharmony_ci	dt->mod_name = modname;
125262306a36Sopenharmony_ci	dt->ddebugs = di->descs;
125362306a36Sopenharmony_ci	dt->num_ddebugs = di->num_descs;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	INIT_LIST_HEAD(&dt->link);
125662306a36Sopenharmony_ci	INIT_LIST_HEAD(&dt->maps);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	if (di->classes && di->num_classes)
125962306a36Sopenharmony_ci		ddebug_attach_module_classes(dt, di->classes, di->num_classes);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	mutex_lock(&ddebug_lock);
126262306a36Sopenharmony_ci	list_add_tail(&dt->link, &ddebug_tables);
126362306a36Sopenharmony_ci	mutex_unlock(&ddebug_lock);
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	vpr_info("%3u debug prints in module %s\n", di->num_descs, modname);
126662306a36Sopenharmony_ci	return 0;
126762306a36Sopenharmony_ci}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci/* helper for ddebug_dyndbg_(boot|module)_param_cb */
127062306a36Sopenharmony_cistatic int ddebug_dyndbg_param_cb(char *param, char *val,
127162306a36Sopenharmony_ci				const char *modname, int on_err)
127262306a36Sopenharmony_ci{
127362306a36Sopenharmony_ci	char *sep;
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	sep = strchr(param, '.');
127662306a36Sopenharmony_ci	if (sep) {
127762306a36Sopenharmony_ci		/* needed only for ddebug_dyndbg_boot_param_cb */
127862306a36Sopenharmony_ci		*sep = '\0';
127962306a36Sopenharmony_ci		modname = param;
128062306a36Sopenharmony_ci		param = sep + 1;
128162306a36Sopenharmony_ci	}
128262306a36Sopenharmony_ci	if (strcmp(param, "dyndbg"))
128362306a36Sopenharmony_ci		return on_err; /* determined by caller */
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	ddebug_exec_queries((val ? val : "+p"), modname);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	return 0; /* query failure shouldn't stop module load */
128862306a36Sopenharmony_ci}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci/* handle both dyndbg and $module.dyndbg params at boot */
129162306a36Sopenharmony_cistatic int ddebug_dyndbg_boot_param_cb(char *param, char *val,
129262306a36Sopenharmony_ci				const char *unused, void *arg)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	vpr_info("%s=\"%s\"\n", param, val);
129562306a36Sopenharmony_ci	return ddebug_dyndbg_param_cb(param, val, NULL, 0);
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci/*
129962306a36Sopenharmony_ci * modprobe foo finds foo.params in boot-args, strips "foo.", and
130062306a36Sopenharmony_ci * passes them to load_module().  This callback gets unknown params,
130162306a36Sopenharmony_ci * processes dyndbg params, rejects others.
130262306a36Sopenharmony_ci */
130362306a36Sopenharmony_ciint ddebug_dyndbg_module_param_cb(char *param, char *val, const char *module)
130462306a36Sopenharmony_ci{
130562306a36Sopenharmony_ci	vpr_info("module: %s %s=\"%s\"\n", module, param, val);
130662306a36Sopenharmony_ci	return ddebug_dyndbg_param_cb(param, val, module, -ENOENT);
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic void ddebug_table_free(struct ddebug_table *dt)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	list_del_init(&dt->link);
131262306a36Sopenharmony_ci	kfree(dt);
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci#ifdef CONFIG_MODULES
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci/*
131862306a36Sopenharmony_ci * Called in response to a module being unloaded.  Removes
131962306a36Sopenharmony_ci * any ddebug_table's which point at the module.
132062306a36Sopenharmony_ci */
132162306a36Sopenharmony_cistatic int ddebug_remove_module(const char *mod_name)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	struct ddebug_table *dt, *nextdt;
132462306a36Sopenharmony_ci	int ret = -ENOENT;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	mutex_lock(&ddebug_lock);
132762306a36Sopenharmony_ci	list_for_each_entry_safe(dt, nextdt, &ddebug_tables, link) {
132862306a36Sopenharmony_ci		if (dt->mod_name == mod_name) {
132962306a36Sopenharmony_ci			ddebug_table_free(dt);
133062306a36Sopenharmony_ci			ret = 0;
133162306a36Sopenharmony_ci			break;
133262306a36Sopenharmony_ci		}
133362306a36Sopenharmony_ci	}
133462306a36Sopenharmony_ci	mutex_unlock(&ddebug_lock);
133562306a36Sopenharmony_ci	if (!ret)
133662306a36Sopenharmony_ci		v2pr_info("removed module \"%s\"\n", mod_name);
133762306a36Sopenharmony_ci	return ret;
133862306a36Sopenharmony_ci}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_cistatic int ddebug_module_notify(struct notifier_block *self, unsigned long val,
134162306a36Sopenharmony_ci				void *data)
134262306a36Sopenharmony_ci{
134362306a36Sopenharmony_ci	struct module *mod = data;
134462306a36Sopenharmony_ci	int ret = 0;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	switch (val) {
134762306a36Sopenharmony_ci	case MODULE_STATE_COMING:
134862306a36Sopenharmony_ci		ret = ddebug_add_module(&mod->dyndbg_info, mod->name);
134962306a36Sopenharmony_ci		if (ret)
135062306a36Sopenharmony_ci			WARN(1, "Failed to allocate memory: dyndbg may not work properly.\n");
135162306a36Sopenharmony_ci		break;
135262306a36Sopenharmony_ci	case MODULE_STATE_GOING:
135362306a36Sopenharmony_ci		ddebug_remove_module(mod->name);
135462306a36Sopenharmony_ci		break;
135562306a36Sopenharmony_ci	}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	return notifier_from_errno(ret);
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic struct notifier_block ddebug_module_nb = {
136162306a36Sopenharmony_ci	.notifier_call = ddebug_module_notify,
136262306a36Sopenharmony_ci	.priority = 0, /* dynamic debug depends on jump label */
136362306a36Sopenharmony_ci};
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci#endif /* CONFIG_MODULES */
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic void ddebug_remove_all_tables(void)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	mutex_lock(&ddebug_lock);
137062306a36Sopenharmony_ci	while (!list_empty(&ddebug_tables)) {
137162306a36Sopenharmony_ci		struct ddebug_table *dt = list_entry(ddebug_tables.next,
137262306a36Sopenharmony_ci						      struct ddebug_table,
137362306a36Sopenharmony_ci						      link);
137462306a36Sopenharmony_ci		ddebug_table_free(dt);
137562306a36Sopenharmony_ci	}
137662306a36Sopenharmony_ci	mutex_unlock(&ddebug_lock);
137762306a36Sopenharmony_ci}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_cistatic __initdata int ddebug_init_success;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_cistatic int __init dynamic_debug_init_control(void)
138262306a36Sopenharmony_ci{
138362306a36Sopenharmony_ci	struct proc_dir_entry *procfs_dir;
138462306a36Sopenharmony_ci	struct dentry *debugfs_dir;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	if (!ddebug_init_success)
138762306a36Sopenharmony_ci		return -ENODEV;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	/* Create the control file in debugfs if it is enabled */
139062306a36Sopenharmony_ci	if (debugfs_initialized()) {
139162306a36Sopenharmony_ci		debugfs_dir = debugfs_create_dir("dynamic_debug", NULL);
139262306a36Sopenharmony_ci		debugfs_create_file("control", 0644, debugfs_dir, NULL,
139362306a36Sopenharmony_ci				    &ddebug_proc_fops);
139462306a36Sopenharmony_ci	}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	/* Also create the control file in procfs */
139762306a36Sopenharmony_ci	procfs_dir = proc_mkdir("dynamic_debug", NULL);
139862306a36Sopenharmony_ci	if (procfs_dir)
139962306a36Sopenharmony_ci		proc_create("control", 0644, procfs_dir, &proc_fops);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	return 0;
140262306a36Sopenharmony_ci}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_cistatic int __init dynamic_debug_init(void)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	struct _ddebug *iter, *iter_mod_start;
140762306a36Sopenharmony_ci	int ret, i, mod_sites, mod_ct;
140862306a36Sopenharmony_ci	const char *modname;
140962306a36Sopenharmony_ci	char *cmdline;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	struct _ddebug_info di = {
141262306a36Sopenharmony_ci		.descs = __start___dyndbg,
141362306a36Sopenharmony_ci		.classes = __start___dyndbg_classes,
141462306a36Sopenharmony_ci		.num_descs = __stop___dyndbg - __start___dyndbg,
141562306a36Sopenharmony_ci		.num_classes = __stop___dyndbg_classes - __start___dyndbg_classes,
141662306a36Sopenharmony_ci	};
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci#ifdef CONFIG_MODULES
141962306a36Sopenharmony_ci	ret = register_module_notifier(&ddebug_module_nb);
142062306a36Sopenharmony_ci	if (ret) {
142162306a36Sopenharmony_ci		pr_warn("Failed to register dynamic debug module notifier\n");
142262306a36Sopenharmony_ci		return ret;
142362306a36Sopenharmony_ci	}
142462306a36Sopenharmony_ci#endif /* CONFIG_MODULES */
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	if (&__start___dyndbg == &__stop___dyndbg) {
142762306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_DYNAMIC_DEBUG)) {
142862306a36Sopenharmony_ci			pr_warn("_ddebug table is empty in a CONFIG_DYNAMIC_DEBUG build\n");
142962306a36Sopenharmony_ci			return 1;
143062306a36Sopenharmony_ci		}
143162306a36Sopenharmony_ci		pr_info("Ignore empty _ddebug table in a CONFIG_DYNAMIC_DEBUG_CORE build\n");
143262306a36Sopenharmony_ci		ddebug_init_success = 1;
143362306a36Sopenharmony_ci		return 0;
143462306a36Sopenharmony_ci	}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	iter = iter_mod_start = __start___dyndbg;
143762306a36Sopenharmony_ci	modname = iter->modname;
143862306a36Sopenharmony_ci	i = mod_sites = mod_ct = 0;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	for (; iter < __stop___dyndbg; iter++, i++, mod_sites++) {
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci		if (strcmp(modname, iter->modname)) {
144362306a36Sopenharmony_ci			mod_ct++;
144462306a36Sopenharmony_ci			di.num_descs = mod_sites;
144562306a36Sopenharmony_ci			di.descs = iter_mod_start;
144662306a36Sopenharmony_ci			ret = ddebug_add_module(&di, modname);
144762306a36Sopenharmony_ci			if (ret)
144862306a36Sopenharmony_ci				goto out_err;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci			mod_sites = 0;
145162306a36Sopenharmony_ci			modname = iter->modname;
145262306a36Sopenharmony_ci			iter_mod_start = iter;
145362306a36Sopenharmony_ci		}
145462306a36Sopenharmony_ci	}
145562306a36Sopenharmony_ci	di.num_descs = mod_sites;
145662306a36Sopenharmony_ci	di.descs = iter_mod_start;
145762306a36Sopenharmony_ci	ret = ddebug_add_module(&di, modname);
145862306a36Sopenharmony_ci	if (ret)
145962306a36Sopenharmony_ci		goto out_err;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	ddebug_init_success = 1;
146262306a36Sopenharmony_ci	vpr_info("%d prdebugs in %d modules, %d KiB in ddebug tables, %d kiB in __dyndbg section\n",
146362306a36Sopenharmony_ci		 i, mod_ct, (int)((mod_ct * sizeof(struct ddebug_table)) >> 10),
146462306a36Sopenharmony_ci		 (int)((i * sizeof(struct _ddebug)) >> 10));
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (di.num_classes)
146762306a36Sopenharmony_ci		v2pr_info("  %d builtin ddebug class-maps\n", di.num_classes);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	/* now that ddebug tables are loaded, process all boot args
147062306a36Sopenharmony_ci	 * again to find and activate queries given in dyndbg params.
147162306a36Sopenharmony_ci	 * While this has already been done for known boot params, it
147262306a36Sopenharmony_ci	 * ignored the unknown ones (dyndbg in particular).  Reusing
147362306a36Sopenharmony_ci	 * parse_args avoids ad-hoc parsing.  This will also attempt
147462306a36Sopenharmony_ci	 * to activate queries for not-yet-loaded modules, which is
147562306a36Sopenharmony_ci	 * slightly noisy if verbose, but harmless.
147662306a36Sopenharmony_ci	 */
147762306a36Sopenharmony_ci	cmdline = kstrdup(saved_command_line, GFP_KERNEL);
147862306a36Sopenharmony_ci	parse_args("dyndbg params", cmdline, NULL,
147962306a36Sopenharmony_ci		   0, 0, 0, NULL, &ddebug_dyndbg_boot_param_cb);
148062306a36Sopenharmony_ci	kfree(cmdline);
148162306a36Sopenharmony_ci	return 0;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ciout_err:
148462306a36Sopenharmony_ci	ddebug_remove_all_tables();
148562306a36Sopenharmony_ci	return 0;
148662306a36Sopenharmony_ci}
148762306a36Sopenharmony_ci/* Allow early initialization for boot messages via boot param */
148862306a36Sopenharmony_ciearly_initcall(dynamic_debug_init);
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci/* Debugfs setup must be done later */
149162306a36Sopenharmony_cifs_initcall(dynamic_debug_init_control);
1492