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