]>
Commit | Line | Data |
---|---|---|
e9d376f0 JB |
1 | /* |
2 | * lib/dynamic_debug.c | |
3 | * | |
4 | * make pr_debug()/dev_dbg() calls runtime configurable based upon their | |
5 | * source module. | |
6 | * | |
7 | * Copyright (C) 2008 Jason Baron <[email protected]> | |
8 | * By Greg Banks <[email protected]> | |
9 | * Copyright (c) 2008 Silicon Graphics Inc. All Rights Reserved. | |
8ba6ebf5 | 10 | * Copyright (C) 2011 Bart Van Assche. All Rights Reserved. |
578b1e07 | 11 | * Copyright (C) 2013 Du, Changbin <[email protected]> |
e9d376f0 JB |
12 | */ |
13 | ||
5aa9ffbb | 14 | #define pr_fmt(fmt) "dyndbg: " fmt |
4ad275e5 | 15 | |
e9d376f0 JB |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> | |
fef15d2f GKH |
18 | #include <linux/moduleparam.h> |
19 | #include <linux/kallsyms.h> | |
20 | #include <linux/types.h> | |
e9d376f0 | 21 | #include <linux/mutex.h> |
fef15d2f | 22 | #include <linux/proc_fs.h> |
e9d376f0 | 23 | #include <linux/seq_file.h> |
fef15d2f GKH |
24 | #include <linux/list.h> |
25 | #include <linux/sysctl.h> | |
e9d376f0 | 26 | #include <linux/ctype.h> |
fef15d2f | 27 | #include <linux/string.h> |
578b1e07 | 28 | #include <linux/parser.h> |
d338b137 | 29 | #include <linux/string_helpers.h> |
fef15d2f | 30 | #include <linux/uaccess.h> |
e9d376f0 JB |
31 | #include <linux/dynamic_debug.h> |
32 | #include <linux/debugfs.h> | |
5a0e3ad6 | 33 | #include <linux/slab.h> |
fef15d2f | 34 | #include <linux/jump_label.h> |
8ba6ebf5 | 35 | #include <linux/hardirq.h> |
e8d9792a | 36 | #include <linux/sched.h> |
fef15d2f | 37 | #include <linux/device.h> |
ffa10cb4 | 38 | #include <linux/netdevice.h> |
e9d376f0 | 39 | |
923abb9d GP |
40 | #include <rdma/ib_verbs.h> |
41 | ||
e5ebffe1 JC |
42 | extern struct _ddebug __start___dyndbg[]; |
43 | extern struct _ddebug __stop___dyndbg[]; | |
66f4006b JC |
44 | extern struct ddebug_class_map __start___dyndbg_classes[]; |
45 | extern struct ddebug_class_map __stop___dyndbg_classes[]; | |
e9d376f0 | 46 | |
e9d376f0 JB |
47 | struct ddebug_table { |
48 | struct list_head link; | |
3e406b1d | 49 | const char *mod_name; |
e9d376f0 | 50 | unsigned int num_ddebugs; |
e9d376f0 JB |
51 | struct _ddebug *ddebugs; |
52 | }; | |
53 | ||
54 | struct ddebug_query { | |
55 | const char *filename; | |
56 | const char *module; | |
57 | const char *function; | |
58 | const char *format; | |
59 | unsigned int first_lineno, last_lineno; | |
60 | }; | |
61 | ||
62 | struct ddebug_iter { | |
63 | struct ddebug_table *table; | |
773beabb | 64 | int idx; |
e9d376f0 JB |
65 | }; |
66 | ||
84da83a6 JC |
67 | struct flag_settings { |
68 | unsigned int flags; | |
69 | unsigned int mask; | |
70 | }; | |
71 | ||
e9d376f0 JB |
72 | static DEFINE_MUTEX(ddebug_lock); |
73 | static LIST_HEAD(ddebug_tables); | |
f657fd21 | 74 | static int verbose; |
74df138d | 75 | module_param(verbose, int, 0644); |
09ee10ff JC |
76 | MODULE_PARM_DESC(verbose, " dynamic_debug/control processing " |
77 | "( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)"); | |
e9d376f0 | 78 | |
2b678319 JC |
79 | /* Return the path relative to source root */ |
80 | static inline const char *trim_prefix(const char *path) | |
81 | { | |
82 | int skip = strlen(__FILE__) - strlen("lib/dynamic_debug.c"); | |
83 | ||
84 | if (strncmp(path, __FILE__, skip)) | |
85 | skip = 0; /* prefix mismatch, don't skip */ | |
86 | ||
87 | return path + skip; | |
88 | } | |
89 | ||
8ba6ebf5 BVA |
90 | static struct { unsigned flag:8; char opt_char; } opt_array[] = { |
91 | { _DPRINTK_FLAGS_PRINT, 'p' }, | |
92 | { _DPRINTK_FLAGS_INCL_MODNAME, 'm' }, | |
93 | { _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' }, | |
94 | { _DPRINTK_FLAGS_INCL_LINENO, 'l' }, | |
95 | { _DPRINTK_FLAGS_INCL_TID, 't' }, | |
5ca7d2a6 | 96 | { _DPRINTK_FLAGS_NONE, '_' }, |
8ba6ebf5 BVA |
97 | }; |
98 | ||
f678ce8c JC |
99 | struct flagsbuf { char buf[ARRAY_SIZE(opt_array)+1]; }; |
100 | ||
e9d376f0 | 101 | /* format a string into buf[] which describes the _ddebug's flags */ |
f678ce8c | 102 | static char *ddebug_describe_flags(unsigned int flags, struct flagsbuf *fb) |
e9d376f0 | 103 | { |
f678ce8c | 104 | char *p = fb->buf; |
8ba6ebf5 | 105 | int i; |
e9d376f0 | 106 | |
8ba6ebf5 | 107 | for (i = 0; i < ARRAY_SIZE(opt_array); ++i) |
f678ce8c | 108 | if (flags & opt_array[i].flag) |
8ba6ebf5 | 109 | *p++ = opt_array[i].opt_char; |
f678ce8c | 110 | if (p == fb->buf) |
5ca7d2a6 | 111 | *p++ = '_'; |
e9d376f0 JB |
112 | *p = '\0'; |
113 | ||
f678ce8c | 114 | return fb->buf; |
e9d376f0 JB |
115 | } |
116 | ||
481c0e33 | 117 | #define vnpr_info(lvl, fmt, ...) \ |
b8ccd5de | 118 | do { \ |
481c0e33 | 119 | if (verbose >= lvl) \ |
f657fd21 | 120 | pr_info(fmt, ##__VA_ARGS__); \ |
574b3725 JC |
121 | } while (0) |
122 | ||
481c0e33 JC |
123 | #define vpr_info(fmt, ...) vnpr_info(1, fmt, ##__VA_ARGS__) |
124 | #define v2pr_info(fmt, ...) vnpr_info(2, fmt, ##__VA_ARGS__) | |
09ee10ff JC |
125 | #define v3pr_info(fmt, ...) vnpr_info(3, fmt, ##__VA_ARGS__) |
126 | #define v4pr_info(fmt, ...) vnpr_info(4, fmt, ##__VA_ARGS__) | |
481c0e33 | 127 | |
f657fd21 JP |
128 | static void vpr_info_dq(const struct ddebug_query *query, const char *msg) |
129 | { | |
130 | /* trim any trailing newlines */ | |
131 | int fmtlen = 0; | |
132 | ||
133 | if (query->format) { | |
134 | fmtlen = strlen(query->format); | |
135 | while (fmtlen && query->format[fmtlen - 1] == '\n') | |
136 | fmtlen--; | |
137 | } | |
138 | ||
09ee10ff | 139 | v3pr_info("%s: func=\"%s\" file=\"%s\" module=\"%s\" format=\"%.*s\" lineno=%u-%u\n", |
f657fd21 | 140 | msg, |
f62fc08f JC |
141 | query->function ?: "", |
142 | query->filename ?: "", | |
143 | query->module ?: "", | |
144 | fmtlen, query->format ?: "", | |
f657fd21 JP |
145 | query->first_lineno, query->last_lineno); |
146 | } | |
147 | ||
e9d376f0 | 148 | /* |
85f7f6c0 JC |
149 | * Search the tables for _ddebug's which match the given `query' and |
150 | * apply the `flags' and `mask' to them. Returns number of matching | |
151 | * callsites, normally the same as number of changes. If verbose, | |
152 | * logs the changes. Takes ddebug_lock. | |
e9d376f0 | 153 | */ |
85f7f6c0 | 154 | static int ddebug_change(const struct ddebug_query *query, |
84da83a6 | 155 | struct flag_settings *modifiers) |
e9d376f0 JB |
156 | { |
157 | int i; | |
158 | struct ddebug_table *dt; | |
159 | unsigned int newflags; | |
160 | unsigned int nfound = 0; | |
bfa3ca44 | 161 | struct flagsbuf fbuf, nbuf; |
e9d376f0 JB |
162 | |
163 | /* search for matching ddebugs */ | |
164 | mutex_lock(&ddebug_lock); | |
165 | list_for_each_entry(dt, &ddebug_tables, link) { | |
166 | ||
167 | /* match against the module name */ | |
578b1e07 CD |
168 | if (query->module && |
169 | !match_wildcard(query->module, dt->mod_name)) | |
e9d376f0 JB |
170 | continue; |
171 | ||
f657fd21 | 172 | for (i = 0; i < dt->num_ddebugs; i++) { |
e9d376f0 JB |
173 | struct _ddebug *dp = &dt->ddebugs[i]; |
174 | ||
175 | /* match against the source filename */ | |
d6a238d2 | 176 | if (query->filename && |
578b1e07 CD |
177 | !match_wildcard(query->filename, dp->filename) && |
178 | !match_wildcard(query->filename, | |
179 | kbasename(dp->filename)) && | |
180 | !match_wildcard(query->filename, | |
181 | trim_prefix(dp->filename))) | |
e9d376f0 JB |
182 | continue; |
183 | ||
184 | /* match against the function */ | |
d6a238d2 | 185 | if (query->function && |
578b1e07 | 186 | !match_wildcard(query->function, dp->function)) |
e9d376f0 JB |
187 | continue; |
188 | ||
189 | /* match against the format */ | |
4b334484 JC |
190 | if (query->format) { |
191 | if (*query->format == '^') { | |
192 | char *p; | |
193 | /* anchored search. match must be at beginning */ | |
194 | p = strstr(dp->format, query->format+1); | |
195 | if (p != dp->format) | |
196 | continue; | |
197 | } else if (!strstr(dp->format, query->format)) | |
198 | continue; | |
199 | } | |
e9d376f0 JB |
200 | |
201 | /* match against the line number range */ | |
202 | if (query->first_lineno && | |
203 | dp->lineno < query->first_lineno) | |
204 | continue; | |
205 | if (query->last_lineno && | |
206 | dp->lineno > query->last_lineno) | |
207 | continue; | |
208 | ||
209 | nfound++; | |
210 | ||
84da83a6 | 211 | newflags = (dp->flags & modifiers->mask) | modifiers->flags; |
e9d376f0 JB |
212 | if (newflags == dp->flags) |
213 | continue; | |
e9666d10 | 214 | #ifdef CONFIG_JUMP_LABEL |
9049fc74 | 215 | if (dp->flags & _DPRINTK_FLAGS_PRINT) { |
ee879be3 | 216 | if (!(newflags & _DPRINTK_FLAGS_PRINT)) |
9049fc74 | 217 | static_branch_disable(&dp->key.dd_key_true); |
ee879be3 | 218 | } else if (newflags & _DPRINTK_FLAGS_PRINT) { |
9049fc74 | 219 | static_branch_enable(&dp->key.dd_key_true); |
ee879be3 | 220 | } |
9049fc74 | 221 | #endif |
bfa3ca44 JC |
222 | v4pr_info("changed %s:%d [%s]%s %s => %s\n", |
223 | trim_prefix(dp->filename), dp->lineno, | |
224 | dt->mod_name, dp->function, | |
225 | ddebug_describe_flags(dp->flags, &fbuf), | |
226 | ddebug_describe_flags(newflags, &nbuf)); | |
e9d376f0 | 227 | dp->flags = newflags; |
e9d376f0 JB |
228 | } |
229 | } | |
230 | mutex_unlock(&ddebug_lock); | |
231 | ||
232 | if (!nfound && verbose) | |
4ad275e5 | 233 | pr_info("no matches for query\n"); |
85f7f6c0 JC |
234 | |
235 | return nfound; | |
e9d376f0 JB |
236 | } |
237 | ||
e9d376f0 JB |
238 | /* |
239 | * Split the buffer `buf' into space-separated words. | |
9898abb3 GB |
240 | * Handles simple " and ' quoting, i.e. without nested, |
241 | * embedded or escaped \". Return the number of words | |
242 | * or <0 on error. | |
e9d376f0 JB |
243 | */ |
244 | static int ddebug_tokenize(char *buf, char *words[], int maxwords) | |
245 | { | |
246 | int nwords = 0; | |
247 | ||
9898abb3 GB |
248 | while (*buf) { |
249 | char *end; | |
250 | ||
251 | /* Skip leading whitespace */ | |
e7d2860b | 252 | buf = skip_spaces(buf); |
9898abb3 GB |
253 | if (!*buf) |
254 | break; /* oh, it was trailing whitespace */ | |
8bd6026e JC |
255 | if (*buf == '#') |
256 | break; /* token starts comment, skip rest of line */ | |
9898abb3 | 257 | |
07100be7 | 258 | /* find `end' of word, whitespace separated or quoted */ |
9898abb3 GB |
259 | if (*buf == '"' || *buf == '\'') { |
260 | int quote = *buf++; | |
f657fd21 | 261 | for (end = buf; *end && *end != quote; end++) |
9898abb3 | 262 | ; |
18c216c5 JC |
263 | if (!*end) { |
264 | pr_err("unclosed quote: %s\n", buf); | |
9898abb3 | 265 | return -EINVAL; /* unclosed quote */ |
18c216c5 | 266 | } |
9898abb3 | 267 | } else { |
7f6e1f30 | 268 | for (end = buf; *end && !isspace(*end); end++) |
9898abb3 GB |
269 | ; |
270 | BUG_ON(end == buf); | |
271 | } | |
9898abb3 | 272 | |
07100be7 | 273 | /* `buf' is start of word, `end' is one past its end */ |
18c216c5 JC |
274 | if (nwords == maxwords) { |
275 | pr_err("too many words, legal max <=%d\n", maxwords); | |
9898abb3 | 276 | return -EINVAL; /* ran out of words[] before bytes */ |
18c216c5 | 277 | } |
9898abb3 GB |
278 | if (*end) |
279 | *end++ = '\0'; /* terminate the word */ | |
280 | words[nwords++] = buf; | |
281 | buf = end; | |
282 | } | |
e9d376f0 | 283 | |
09ee10ff | 284 | if (verbose >= 3) { |
e9d376f0 | 285 | int i; |
4ad275e5 | 286 | pr_info("split into words:"); |
f657fd21 | 287 | for (i = 0; i < nwords; i++) |
4ad275e5 JP |
288 | pr_cont(" \"%s\"", words[i]); |
289 | pr_cont("\n"); | |
e9d376f0 JB |
290 | } |
291 | ||
292 | return nwords; | |
293 | } | |
294 | ||
295 | /* | |
296 | * Parse a single line number. Note that the empty string "" | |
297 | * is treated as a special case and converted to zero, which | |
298 | * is later treated as a "don't care" value. | |
299 | */ | |
300 | static inline int parse_lineno(const char *str, unsigned int *val) | |
301 | { | |
e9d376f0 JB |
302 | BUG_ON(str == NULL); |
303 | if (*str == '\0') { | |
304 | *val = 0; | |
305 | return 0; | |
306 | } | |
4592599a | 307 | if (kstrtouint(str, 10, val) < 0) { |
18c216c5 JC |
308 | pr_err("bad line-number: %s\n", str); |
309 | return -EINVAL; | |
310 | } | |
311 | return 0; | |
e9d376f0 JB |
312 | } |
313 | ||
8037072d JC |
314 | static int parse_linerange(struct ddebug_query *query, const char *first) |
315 | { | |
316 | char *last = strchr(first, '-'); | |
317 | ||
318 | if (query->first_lineno || query->last_lineno) { | |
319 | pr_err("match-spec: line used 2x\n"); | |
320 | return -EINVAL; | |
321 | } | |
322 | if (last) | |
323 | *last++ = '\0'; | |
324 | if (parse_lineno(first, &query->first_lineno) < 0) | |
325 | return -EINVAL; | |
326 | if (last) { | |
327 | /* range <first>-<last> */ | |
328 | if (parse_lineno(last, &query->last_lineno) < 0) | |
329 | return -EINVAL; | |
330 | ||
331 | /* special case for last lineno not specified */ | |
332 | if (query->last_lineno == 0) | |
333 | query->last_lineno = UINT_MAX; | |
334 | ||
335 | if (query->last_lineno < query->first_lineno) { | |
336 | pr_err("last-line:%d < 1st-line:%d\n", | |
337 | query->last_lineno, | |
338 | query->first_lineno); | |
339 | return -EINVAL; | |
340 | } | |
341 | } else { | |
342 | query->last_lineno = query->first_lineno; | |
343 | } | |
09ee10ff | 344 | v3pr_info("parsed line %d-%d\n", query->first_lineno, |
8037072d JC |
345 | query->last_lineno); |
346 | return 0; | |
347 | } | |
348 | ||
820874c7 JC |
349 | static int check_set(const char **dest, char *src, char *name) |
350 | { | |
351 | int rc = 0; | |
352 | ||
353 | if (*dest) { | |
354 | rc = -EINVAL; | |
f657fd21 JP |
355 | pr_err("match-spec:%s val:%s overridden by %s\n", |
356 | name, *dest, src); | |
820874c7 JC |
357 | } |
358 | *dest = src; | |
359 | return rc; | |
360 | } | |
361 | ||
e9d376f0 JB |
362 | /* |
363 | * Parse words[] as a ddebug query specification, which is a series | |
952e934d | 364 | * of (keyword, value) pairs chosen from these possibilities: |
e9d376f0 JB |
365 | * |
366 | * func <function-name> | |
367 | * file <full-pathname> | |
368 | * file <base-filename> | |
369 | * module <module-name> | |
370 | * format <escaped-string-to-find-in-format> | |
371 | * line <lineno> | |
372 | * line <first-lineno>-<last-lineno> // where either may be empty | |
820874c7 JC |
373 | * |
374 | * Only 1 of each type is allowed. | |
375 | * Returns 0 on success, <0 on error. | |
e9d376f0 JB |
376 | */ |
377 | static int ddebug_parse_query(char *words[], int nwords, | |
8e59b5cf | 378 | struct ddebug_query *query, const char *modname) |
e9d376f0 JB |
379 | { |
380 | unsigned int i; | |
bd8c154a | 381 | int rc = 0; |
aaebe329 | 382 | char *fline; |
952e934d GKH |
383 | |
384 | /* check we have an even number of words */ | |
385 | if (nwords % 2 != 0) { | |
386 | pr_err("expecting pairs of match-spec <value>\n"); | |
387 | return -EINVAL; | |
388 | } | |
e9d376f0 | 389 | |
952e934d | 390 | for (i = 0; i < nwords; i += 2) { |
e5e5fcef JC |
391 | char *keyword = words[i]; |
392 | char *arg = words[i+1]; | |
393 | ||
394 | if (!strcmp(keyword, "func")) { | |
395 | rc = check_set(&query->function, arg, "func"); | |
396 | } else if (!strcmp(keyword, "file")) { | |
397 | if (check_set(&query->filename, arg, "file")) | |
aaebe329 JC |
398 | return -EINVAL; |
399 | ||
400 | /* tail :$info is function or line-range */ | |
401 | fline = strchr(query->filename, ':'); | |
402 | if (!fline) | |
7b1ae248 | 403 | continue; |
aaebe329 JC |
404 | *fline++ = '\0'; |
405 | if (isalpha(*fline) || *fline == '*' || *fline == '?') { | |
406 | /* take as function name */ | |
407 | if (check_set(&query->function, fline, "func")) | |
408 | return -EINVAL; | |
409 | } else { | |
410 | if (parse_linerange(query, fline)) | |
411 | return -EINVAL; | |
412 | } | |
e5e5fcef JC |
413 | } else if (!strcmp(keyword, "module")) { |
414 | rc = check_set(&query->module, arg, "module"); | |
415 | } else if (!strcmp(keyword, "format")) { | |
416 | string_unescape_inplace(arg, UNESCAPE_SPACE | | |
d338b137 AS |
417 | UNESCAPE_OCTAL | |
418 | UNESCAPE_SPECIAL); | |
e5e5fcef JC |
419 | rc = check_set(&query->format, arg, "format"); |
420 | } else if (!strcmp(keyword, "line")) { | |
421 | if (parse_linerange(query, arg)) | |
e9d376f0 | 422 | return -EINVAL; |
e9d376f0 | 423 | } else { |
e5e5fcef | 424 | pr_err("unknown keyword \"%s\"\n", keyword); |
e9d376f0 JB |
425 | return -EINVAL; |
426 | } | |
820874c7 JC |
427 | if (rc) |
428 | return rc; | |
e9d376f0 | 429 | } |
e75ef56f JC |
430 | if (!query->module && modname) |
431 | /* | |
432 | * support $modname.dyndbg=<multiple queries>, when | |
433 | * not given in the query itself | |
434 | */ | |
435 | query->module = modname; | |
436 | ||
574b3725 | 437 | vpr_info_dq(query, "parsed"); |
e9d376f0 JB |
438 | return 0; |
439 | } | |
440 | ||
441 | /* | |
442 | * Parse `str' as a flags specification, format [-+=][p]+. | |
443 | * Sets up *maskp and *flagsp to be used when changing the | |
444 | * flags fields of matched _ddebug's. Returns 0 on success | |
445 | * or <0 on error. | |
446 | */ | |
84da83a6 | 447 | static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers) |
e9d376f0 | 448 | { |
84da83a6 | 449 | int op, i; |
e9d376f0 JB |
450 | |
451 | switch (*str) { | |
452 | case '+': | |
453 | case '-': | |
454 | case '=': | |
455 | op = *str++; | |
456 | break; | |
457 | default: | |
18c216c5 | 458 | pr_err("bad flag-op %c, at start of %s\n", *str, str); |
e9d376f0 JB |
459 | return -EINVAL; |
460 | } | |
09ee10ff | 461 | v3pr_info("op='%c'\n", op); |
e9d376f0 | 462 | |
f657fd21 | 463 | for (; *str ; ++str) { |
8ba6ebf5 BVA |
464 | for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) { |
465 | if (*str == opt_array[i].opt_char) { | |
84da83a6 | 466 | modifiers->flags |= opt_array[i].flag; |
8ba6ebf5 BVA |
467 | break; |
468 | } | |
e9d376f0 | 469 | } |
18c216c5 | 470 | if (i < 0) { |
0b8f96be | 471 | pr_err("unknown flag '%c'\n", *str); |
8ba6ebf5 | 472 | return -EINVAL; |
18c216c5 | 473 | } |
e9d376f0 | 474 | } |
09ee10ff | 475 | v3pr_info("flags=0x%x\n", modifiers->flags); |
e9d376f0 | 476 | |
84da83a6 | 477 | /* calculate final flags, mask based upon op */ |
e9d376f0 JB |
478 | switch (op) { |
479 | case '=': | |
84da83a6 JC |
480 | /* modifiers->flags already set */ |
481 | modifiers->mask = 0; | |
e9d376f0 JB |
482 | break; |
483 | case '+': | |
84da83a6 | 484 | modifiers->mask = ~0U; |
e9d376f0 JB |
485 | break; |
486 | case '-': | |
84da83a6 JC |
487 | modifiers->mask = ~modifiers->flags; |
488 | modifiers->flags = 0; | |
e9d376f0 JB |
489 | break; |
490 | } | |
09ee10ff | 491 | v3pr_info("*flagsp=0x%x *maskp=0x%x\n", modifiers->flags, modifiers->mask); |
84da83a6 | 492 | |
e9d376f0 JB |
493 | return 0; |
494 | } | |
495 | ||
8e59b5cf | 496 | static int ddebug_exec_query(char *query_string, const char *modname) |
fd89cfb8 | 497 | { |
84da83a6 | 498 | struct flag_settings modifiers = {}; |
9c9d0acb | 499 | struct ddebug_query query = {}; |
fd89cfb8 | 500 | #define MAXWORDS 9 |
85f7f6c0 | 501 | int nwords, nfound; |
fd89cfb8 TR |
502 | char *words[MAXWORDS]; |
503 | ||
504 | nwords = ddebug_tokenize(query_string, words, MAXWORDS); | |
18c216c5 JC |
505 | if (nwords <= 0) { |
506 | pr_err("tokenize failed\n"); | |
fd89cfb8 | 507 | return -EINVAL; |
18c216c5 JC |
508 | } |
509 | /* check flags 1st (last arg) so query is pairs of spec,val */ | |
84da83a6 | 510 | if (ddebug_parse_flags(words[nwords-1], &modifiers)) { |
18c216c5 | 511 | pr_err("flags parse failed\n"); |
fd89cfb8 | 512 | return -EINVAL; |
18c216c5 JC |
513 | } |
514 | if (ddebug_parse_query(words, nwords-1, &query, modname)) { | |
515 | pr_err("query parse failed\n"); | |
fd89cfb8 | 516 | return -EINVAL; |
18c216c5 | 517 | } |
fd89cfb8 | 518 | /* actually go and implement the change */ |
84da83a6 | 519 | nfound = ddebug_change(&query, &modifiers); |
f657fd21 | 520 | vpr_info_dq(&query, nfound ? "applied" : "no-match"); |
85f7f6c0 JC |
521 | |
522 | return nfound; | |
523 | } | |
524 | ||
525 | /* handle multiple queries in query string, continue on error, return | |
526 | last error or number of matching callsites. Module name is either | |
527 | in param (for boot arg) or perhaps in query string. | |
528 | */ | |
a2d375ed | 529 | static int ddebug_exec_queries(char *query, const char *modname) |
85f7f6c0 JC |
530 | { |
531 | char *split; | |
532 | int i, errs = 0, exitcode = 0, rc, nfound = 0; | |
533 | ||
534 | for (i = 0; query; query = split) { | |
535 | split = strpbrk(query, ";\n"); | |
536 | if (split) | |
537 | *split++ = '\0'; | |
538 | ||
539 | query = skip_spaces(query); | |
540 | if (!query || !*query || *query == '#') | |
541 | continue; | |
542 | ||
1f8818e3 | 543 | vpr_info("query %d: \"%s\" mod:%s\n", i, query, modname ?: "*"); |
85f7f6c0 | 544 | |
8e59b5cf | 545 | rc = ddebug_exec_query(query, modname); |
85f7f6c0 JC |
546 | if (rc < 0) { |
547 | errs++; | |
548 | exitcode = rc; | |
f657fd21 | 549 | } else { |
85f7f6c0 | 550 | nfound += rc; |
f657fd21 | 551 | } |
85f7f6c0 JC |
552 | i++; |
553 | } | |
7edde0c8 | 554 | if (i) |
09ee10ff | 555 | v2pr_info("processed %d queries, with %d matches, %d errs\n", |
7edde0c8 | 556 | i, nfound, errs); |
85f7f6c0 JC |
557 | |
558 | if (exitcode) | |
559 | return exitcode; | |
560 | return nfound; | |
fd89cfb8 | 561 | } |
a2d375ed | 562 | |
431625da JB |
563 | #define PREFIX_SIZE 64 |
564 | ||
565 | static int remaining(int wrote) | |
566 | { | |
567 | if (PREFIX_SIZE - wrote > 0) | |
568 | return PREFIX_SIZE - wrote; | |
569 | return 0; | |
570 | } | |
571 | ||
640d1eaf | 572 | static char *__dynamic_emit_prefix(const struct _ddebug *desc, char *buf) |
8ba6ebf5 | 573 | { |
431625da JB |
574 | int pos_after_tid; |
575 | int pos = 0; | |
8ba6ebf5 | 576 | |
431625da | 577 | if (desc->flags & _DPRINTK_FLAGS_INCL_TID) { |
8ba6ebf5 | 578 | if (in_interrupt()) |
798efc60 | 579 | pos += snprintf(buf + pos, remaining(pos), "<intr> "); |
8ba6ebf5 | 580 | else |
431625da | 581 | pos += snprintf(buf + pos, remaining(pos), "[%d] ", |
798efc60 | 582 | task_pid_vnr(current)); |
8ba6ebf5 | 583 | } |
431625da JB |
584 | pos_after_tid = pos; |
585 | if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME) | |
586 | pos += snprintf(buf + pos, remaining(pos), "%s:", | |
798efc60 | 587 | desc->modname); |
431625da JB |
588 | if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME) |
589 | pos += snprintf(buf + pos, remaining(pos), "%s:", | |
798efc60 | 590 | desc->function); |
431625da | 591 | if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO) |
07100be7 | 592 | pos += snprintf(buf + pos, remaining(pos), "%d:", |
798efc60 | 593 | desc->lineno); |
431625da JB |
594 | if (pos - pos_after_tid) |
595 | pos += snprintf(buf + pos, remaining(pos), " "); | |
596 | if (pos >= PREFIX_SIZE) | |
597 | buf[PREFIX_SIZE - 1] = '\0'; | |
6c2140ee | 598 | |
431625da | 599 | return buf; |
6c2140ee JP |
600 | } |
601 | ||
640d1eaf JC |
602 | static inline char *dynamic_emit_prefix(struct _ddebug *desc, char *buf) |
603 | { | |
604 | if (unlikely(desc->flags & _DPRINTK_FLAGS_INCL_ANY)) | |
605 | return __dynamic_emit_prefix(desc, buf); | |
606 | return buf; | |
607 | } | |
608 | ||
906d2015 | 609 | void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...) |
8ba6ebf5 BVA |
610 | { |
611 | va_list args; | |
431625da | 612 | struct va_format vaf; |
640d1eaf | 613 | char buf[PREFIX_SIZE] = ""; |
8ba6ebf5 BVA |
614 | |
615 | BUG_ON(!descriptor); | |
616 | BUG_ON(!fmt); | |
617 | ||
618 | va_start(args, fmt); | |
798efc60 | 619 | |
431625da JB |
620 | vaf.fmt = fmt; |
621 | vaf.va = &args; | |
798efc60 | 622 | |
906d2015 | 623 | printk(KERN_DEBUG "%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf); |
798efc60 | 624 | |
8ba6ebf5 | 625 | va_end(args); |
8ba6ebf5 BVA |
626 | } |
627 | EXPORT_SYMBOL(__dynamic_pr_debug); | |
628 | ||
906d2015 | 629 | void __dynamic_dev_dbg(struct _ddebug *descriptor, |
cbc46635 JP |
630 | const struct device *dev, const char *fmt, ...) |
631 | { | |
632 | struct va_format vaf; | |
633 | va_list args; | |
cbc46635 JP |
634 | |
635 | BUG_ON(!descriptor); | |
636 | BUG_ON(!fmt); | |
637 | ||
638 | va_start(args, fmt); | |
798efc60 | 639 | |
cbc46635 JP |
640 | vaf.fmt = fmt; |
641 | vaf.va = &args; | |
798efc60 JP |
642 | |
643 | if (!dev) { | |
906d2015 | 644 | printk(KERN_DEBUG "(NULL device *): %pV", &vaf); |
798efc60 | 645 | } else { |
640d1eaf | 646 | char buf[PREFIX_SIZE] = ""; |
798efc60 | 647 | |
a39d4a85 | 648 | dev_printk_emit(LOGLEVEL_DEBUG, dev, "%s%s %s: %pV", |
906d2015 JP |
649 | dynamic_emit_prefix(descriptor, buf), |
650 | dev_driver_string(dev), dev_name(dev), | |
651 | &vaf); | |
798efc60 JP |
652 | } |
653 | ||
cbc46635 | 654 | va_end(args); |
cbc46635 JP |
655 | } |
656 | EXPORT_SYMBOL(__dynamic_dev_dbg); | |
657 | ||
0feefd97 JB |
658 | #ifdef CONFIG_NET |
659 | ||
906d2015 JP |
660 | void __dynamic_netdev_dbg(struct _ddebug *descriptor, |
661 | const struct net_device *dev, const char *fmt, ...) | |
ffa10cb4 JB |
662 | { |
663 | struct va_format vaf; | |
664 | va_list args; | |
ffa10cb4 JB |
665 | |
666 | BUG_ON(!descriptor); | |
667 | BUG_ON(!fmt); | |
668 | ||
669 | va_start(args, fmt); | |
b004ff49 | 670 | |
ffa10cb4 JB |
671 | vaf.fmt = fmt; |
672 | vaf.va = &args; | |
b004ff49 JP |
673 | |
674 | if (dev && dev->dev.parent) { | |
640d1eaf | 675 | char buf[PREFIX_SIZE] = ""; |
666f355f | 676 | |
a39d4a85 | 677 | dev_printk_emit(LOGLEVEL_DEBUG, dev->dev.parent, |
906d2015 JP |
678 | "%s%s %s %s%s: %pV", |
679 | dynamic_emit_prefix(descriptor, buf), | |
680 | dev_driver_string(dev->dev.parent), | |
681 | dev_name(dev->dev.parent), | |
682 | netdev_name(dev), netdev_reg_state(dev), | |
683 | &vaf); | |
b004ff49 | 684 | } else if (dev) { |
906d2015 JP |
685 | printk(KERN_DEBUG "%s%s: %pV", netdev_name(dev), |
686 | netdev_reg_state(dev), &vaf); | |
b004ff49 | 687 | } else { |
906d2015 | 688 | printk(KERN_DEBUG "(NULL net_device): %pV", &vaf); |
b004ff49 JP |
689 | } |
690 | ||
ffa10cb4 | 691 | va_end(args); |
ffa10cb4 JB |
692 | } |
693 | EXPORT_SYMBOL(__dynamic_netdev_dbg); | |
694 | ||
0feefd97 JB |
695 | #endif |
696 | ||
923abb9d GP |
697 | #if IS_ENABLED(CONFIG_INFINIBAND) |
698 | ||
699 | void __dynamic_ibdev_dbg(struct _ddebug *descriptor, | |
700 | const struct ib_device *ibdev, const char *fmt, ...) | |
701 | { | |
702 | struct va_format vaf; | |
703 | va_list args; | |
704 | ||
705 | va_start(args, fmt); | |
706 | ||
707 | vaf.fmt = fmt; | |
708 | vaf.va = &args; | |
709 | ||
710 | if (ibdev && ibdev->dev.parent) { | |
640d1eaf | 711 | char buf[PREFIX_SIZE] = ""; |
923abb9d GP |
712 | |
713 | dev_printk_emit(LOGLEVEL_DEBUG, ibdev->dev.parent, | |
714 | "%s%s %s %s: %pV", | |
715 | dynamic_emit_prefix(descriptor, buf), | |
716 | dev_driver_string(ibdev->dev.parent), | |
717 | dev_name(ibdev->dev.parent), | |
718 | dev_name(&ibdev->dev), | |
719 | &vaf); | |
720 | } else if (ibdev) { | |
721 | printk(KERN_DEBUG "%s: %pV", dev_name(&ibdev->dev), &vaf); | |
722 | } else { | |
723 | printk(KERN_DEBUG "(NULL ib_device): %pV", &vaf); | |
724 | } | |
725 | ||
726 | va_end(args); | |
727 | } | |
728 | EXPORT_SYMBOL(__dynamic_ibdev_dbg); | |
729 | ||
730 | #endif | |
731 | ||
5ca17397 AH |
732 | /* |
733 | * Install a noop handler to make dyndbg look like a normal kernel cli param. | |
734 | * This avoids warnings about dyndbg being an unknown cli param when supplied | |
735 | * by a user. | |
736 | */ | |
737 | static __init int dyndbg_setup(char *str) | |
738 | { | |
739 | return 1; | |
740 | } | |
741 | ||
742 | __setup("dyndbg=", dyndbg_setup); | |
743 | ||
e9d376f0 | 744 | /* |
231821d4 | 745 | * File_ops->write method for <debugfs>/dynamic_debug/control. Gathers the |
e9d376f0 JB |
746 | * command text from userspace, parses and executes it. |
747 | */ | |
7281491c | 748 | #define USER_BUF_PAGE 4096 |
e9d376f0 JB |
749 | static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, |
750 | size_t len, loff_t *offp) | |
751 | { | |
7281491c | 752 | char *tmpbuf; |
fd89cfb8 | 753 | int ret; |
e9d376f0 JB |
754 | |
755 | if (len == 0) | |
756 | return 0; | |
7281491c JC |
757 | if (len > USER_BUF_PAGE - 1) { |
758 | pr_warn("expected <%d bytes into control\n", USER_BUF_PAGE); | |
e9d376f0 | 759 | return -E2BIG; |
7281491c | 760 | } |
16e5c1fc AV |
761 | tmpbuf = memdup_user_nul(ubuf, len); |
762 | if (IS_ERR(tmpbuf)) | |
763 | return PTR_ERR(tmpbuf); | |
09ee10ff | 764 | v2pr_info("read %zu bytes from userspace\n", len); |
e9d376f0 | 765 | |
8e59b5cf | 766 | ret = ddebug_exec_queries(tmpbuf, NULL); |
7281491c | 767 | kfree(tmpbuf); |
85f7f6c0 | 768 | if (ret < 0) |
fd89cfb8 | 769 | return ret; |
e9d376f0 JB |
770 | |
771 | *offp += len; | |
772 | return len; | |
773 | } | |
774 | ||
775 | /* | |
776 | * Set the iterator to point to the first _ddebug object | |
777 | * and return a pointer to that first object. Returns | |
778 | * NULL if there are no _ddebugs at all. | |
779 | */ | |
780 | static struct _ddebug *ddebug_iter_first(struct ddebug_iter *iter) | |
781 | { | |
782 | if (list_empty(&ddebug_tables)) { | |
783 | iter->table = NULL; | |
e9d376f0 JB |
784 | return NULL; |
785 | } | |
786 | iter->table = list_entry(ddebug_tables.next, | |
787 | struct ddebug_table, link); | |
773beabb JC |
788 | iter->idx = iter->table->num_ddebugs; |
789 | return &iter->table->ddebugs[--iter->idx]; | |
e9d376f0 JB |
790 | } |
791 | ||
792 | /* | |
793 | * Advance the iterator to point to the next _ddebug | |
794 | * object from the one the iterator currently points at, | |
795 | * and returns a pointer to the new _ddebug. Returns | |
796 | * NULL if the iterator has seen all the _ddebugs. | |
797 | */ | |
798 | static struct _ddebug *ddebug_iter_next(struct ddebug_iter *iter) | |
799 | { | |
800 | if (iter->table == NULL) | |
801 | return NULL; | |
773beabb | 802 | if (--iter->idx < 0) { |
e9d376f0 | 803 | /* iterate to next table */ |
e9d376f0 JB |
804 | if (list_is_last(&iter->table->link, &ddebug_tables)) { |
805 | iter->table = NULL; | |
806 | return NULL; | |
807 | } | |
808 | iter->table = list_entry(iter->table->link.next, | |
809 | struct ddebug_table, link); | |
773beabb JC |
810 | iter->idx = iter->table->num_ddebugs; |
811 | --iter->idx; | |
e9d376f0 JB |
812 | } |
813 | return &iter->table->ddebugs[iter->idx]; | |
814 | } | |
815 | ||
816 | /* | |
817 | * Seq_ops start method. Called at the start of every | |
818 | * read() call from userspace. Takes the ddebug_lock and | |
819 | * seeks the seq_file's iterator to the given position. | |
820 | */ | |
821 | static void *ddebug_proc_start(struct seq_file *m, loff_t *pos) | |
822 | { | |
823 | struct ddebug_iter *iter = m->private; | |
824 | struct _ddebug *dp; | |
825 | int n = *pos; | |
826 | ||
e9d376f0 JB |
827 | mutex_lock(&ddebug_lock); |
828 | ||
829 | if (!n) | |
830 | return SEQ_START_TOKEN; | |
831 | if (n < 0) | |
832 | return NULL; | |
833 | dp = ddebug_iter_first(iter); | |
834 | while (dp != NULL && --n > 0) | |
835 | dp = ddebug_iter_next(iter); | |
836 | return dp; | |
837 | } | |
838 | ||
839 | /* | |
840 | * Seq_ops next method. Called several times within a read() | |
841 | * call from userspace, with ddebug_lock held. Walks to the | |
842 | * next _ddebug object with a special case for the header line. | |
843 | */ | |
844 | static void *ddebug_proc_next(struct seq_file *m, void *p, loff_t *pos) | |
845 | { | |
846 | struct ddebug_iter *iter = m->private; | |
847 | struct _ddebug *dp; | |
848 | ||
e9d376f0 JB |
849 | if (p == SEQ_START_TOKEN) |
850 | dp = ddebug_iter_first(iter); | |
851 | else | |
852 | dp = ddebug_iter_next(iter); | |
853 | ++*pos; | |
854 | return dp; | |
855 | } | |
856 | ||
857 | /* | |
858 | * Seq_ops show method. Called several times within a read() | |
859 | * call from userspace, with ddebug_lock held. Formats the | |
860 | * current _ddebug as a single human-readable line, with a | |
861 | * special case for the header line. | |
862 | */ | |
863 | static int ddebug_proc_show(struct seq_file *m, void *p) | |
864 | { | |
865 | struct ddebug_iter *iter = m->private; | |
866 | struct _ddebug *dp = p; | |
f678ce8c | 867 | struct flagsbuf flags; |
e9d376f0 | 868 | |
e9d376f0 JB |
869 | if (p == SEQ_START_TOKEN) { |
870 | seq_puts(m, | |
f657fd21 | 871 | "# filename:lineno [module]function flags format\n"); |
e9d376f0 JB |
872 | return 0; |
873 | } | |
874 | ||
5ca7d2a6 | 875 | seq_printf(m, "%s:%u [%s]%s =%s \"", |
f657fd21 JP |
876 | trim_prefix(dp->filename), dp->lineno, |
877 | iter->table->mod_name, dp->function, | |
f678ce8c | 878 | ddebug_describe_flags(dp->flags, &flags)); |
47ea6f99 | 879 | seq_escape_str(m, dp->format, ESCAPE_SPACE, "\t\r\n\""); |
e9d376f0 JB |
880 | seq_puts(m, "\"\n"); |
881 | ||
882 | return 0; | |
883 | } | |
884 | ||
885 | /* | |
886 | * Seq_ops stop method. Called at the end of each read() | |
887 | * call from userspace. Drops ddebug_lock. | |
888 | */ | |
889 | static void ddebug_proc_stop(struct seq_file *m, void *p) | |
890 | { | |
e9d376f0 JB |
891 | mutex_unlock(&ddebug_lock); |
892 | } | |
893 | ||
894 | static const struct seq_operations ddebug_proc_seqops = { | |
895 | .start = ddebug_proc_start, | |
896 | .next = ddebug_proc_next, | |
897 | .show = ddebug_proc_show, | |
898 | .stop = ddebug_proc_stop | |
899 | }; | |
900 | ||
e9d376f0 JB |
901 | static int ddebug_proc_open(struct inode *inode, struct file *file) |
902 | { | |
4bad78c5 RJ |
903 | return seq_open_private(file, &ddebug_proc_seqops, |
904 | sizeof(struct ddebug_iter)); | |
e9d376f0 JB |
905 | } |
906 | ||
907 | static const struct file_operations ddebug_proc_fops = { | |
908 | .owner = THIS_MODULE, | |
909 | .open = ddebug_proc_open, | |
910 | .read = seq_read, | |
911 | .llseek = seq_lseek, | |
912 | .release = seq_release_private, | |
913 | .write = ddebug_proc_write | |
914 | }; | |
915 | ||
239a5791 GKH |
916 | static const struct proc_ops proc_fops = { |
917 | .proc_open = ddebug_proc_open, | |
918 | .proc_read = seq_read, | |
919 | .proc_lseek = seq_lseek, | |
920 | .proc_release = seq_release_private, | |
921 | .proc_write = ddebug_proc_write | |
922 | }; | |
923 | ||
e9d376f0 JB |
924 | /* |
925 | * Allocate a new ddebug_table for the given module | |
926 | * and add it to the global list. | |
927 | */ | |
b7b4eebd JC |
928 | static int __ddebug_add_module(struct _ddebug_info *di, unsigned int base, |
929 | const char *modname) | |
e9d376f0 JB |
930 | { |
931 | struct ddebug_table *dt; | |
e9d376f0 | 932 | |
b7b4eebd JC |
933 | v3pr_info("add-module: %s.%d sites\n", modname, di->num_descs); |
934 | if (!di->num_descs) { | |
935 | v3pr_info(" skip %s\n", modname); | |
936 | return 0; | |
937 | } | |
938 | ||
e9d376f0 | 939 | dt = kzalloc(sizeof(*dt), GFP_KERNEL); |
513770f5 | 940 | if (dt == NULL) { |
b7b4eebd | 941 | pr_err("error adding module: %s\n", modname); |
e9d376f0 | 942 | return -ENOMEM; |
513770f5 | 943 | } |
cdf6d006 RV |
944 | /* |
945 | * For built-in modules, name lives in .rodata and is | |
946 | * immortal. For loaded modules, name points at the name[] | |
947 | * member of struct module, which lives at least as long as | |
948 | * this struct ddebug_table. | |
949 | */ | |
b7b4eebd JC |
950 | dt->mod_name = modname; |
951 | dt->ddebugs = di->descs; | |
952 | dt->num_ddebugs = di->num_descs; | |
953 | ||
954 | INIT_LIST_HEAD(&dt->link); | |
e9d376f0 JB |
955 | |
956 | mutex_lock(&ddebug_lock); | |
2ad556f7 | 957 | list_add_tail(&dt->link, &ddebug_tables); |
e9d376f0 JB |
958 | mutex_unlock(&ddebug_lock); |
959 | ||
b7b4eebd | 960 | vpr_info("%3u debug prints in module %s\n", di->num_descs, modname); |
e9d376f0 JB |
961 | return 0; |
962 | } | |
e9d376f0 | 963 | |
b7b4eebd JC |
964 | int ddebug_add_module(struct _ddebug_info *di, const char *modname) |
965 | { | |
966 | return __ddebug_add_module(di, 0, modname); | |
967 | } | |
968 | ||
6ab676e9 JC |
969 | /* helper for ddebug_dyndbg_(boot|module)_param_cb */ |
970 | static int ddebug_dyndbg_param_cb(char *param, char *val, | |
971 | const char *modname, int on_err) | |
b48420c1 | 972 | { |
b48420c1 JC |
973 | char *sep; |
974 | ||
975 | sep = strchr(param, '.'); | |
976 | if (sep) { | |
6ab676e9 | 977 | /* needed only for ddebug_dyndbg_boot_param_cb */ |
b48420c1 JC |
978 | *sep = '\0'; |
979 | modname = param; | |
980 | param = sep + 1; | |
981 | } | |
982 | if (strcmp(param, "dyndbg")) | |
6ab676e9 | 983 | return on_err; /* determined by caller */ |
b48420c1 | 984 | |
8e59b5cf JC |
985 | ddebug_exec_queries((val ? val : "+p"), modname); |
986 | ||
9dbbc3b9 | 987 | return 0; /* query failure shouldn't stop module load */ |
b48420c1 JC |
988 | } |
989 | ||
6ab676e9 JC |
990 | /* handle both dyndbg and $module.dyndbg params at boot */ |
991 | static int ddebug_dyndbg_boot_param_cb(char *param, char *val, | |
ecc86170 | 992 | const char *unused, void *arg) |
b48420c1 | 993 | { |
6ab676e9 JC |
994 | vpr_info("%s=\"%s\"\n", param, val); |
995 | return ddebug_dyndbg_param_cb(param, val, NULL, 0); | |
996 | } | |
b48420c1 | 997 | |
6ab676e9 JC |
998 | /* |
999 | * modprobe foo finds foo.params in boot-args, strips "foo.", and | |
1000 | * passes them to load_module(). This callback gets unknown params, | |
1001 | * processes dyndbg params, rejects others. | |
1002 | */ | |
1003 | int ddebug_dyndbg_module_param_cb(char *param, char *val, const char *module) | |
1004 | { | |
1005 | vpr_info("module: %s %s=\"%s\"\n", module, param, val); | |
1006 | return ddebug_dyndbg_param_cb(param, val, module, -ENOENT); | |
b48420c1 JC |
1007 | } |
1008 | ||
e9d376f0 JB |
1009 | static void ddebug_table_free(struct ddebug_table *dt) |
1010 | { | |
1011 | list_del_init(&dt->link); | |
e9d376f0 JB |
1012 | kfree(dt); |
1013 | } | |
1014 | ||
1015 | /* | |
1016 | * Called in response to a module being unloaded. Removes | |
1017 | * any ddebug_table's which point at the module. | |
1018 | */ | |
ff49d74a | 1019 | int ddebug_remove_module(const char *mod_name) |
e9d376f0 JB |
1020 | { |
1021 | struct ddebug_table *dt, *nextdt; | |
1022 | int ret = -ENOENT; | |
1023 | ||
e9d376f0 JB |
1024 | mutex_lock(&ddebug_lock); |
1025 | list_for_each_entry_safe(dt, nextdt, &ddebug_tables, link) { | |
4573fe15 | 1026 | if (dt->mod_name == mod_name) { |
e9d376f0 JB |
1027 | ddebug_table_free(dt); |
1028 | ret = 0; | |
4573fe15 | 1029 | break; |
e9d376f0 JB |
1030 | } |
1031 | } | |
1032 | mutex_unlock(&ddebug_lock); | |
7a5e202d JC |
1033 | if (!ret) |
1034 | v2pr_info("removed module \"%s\"\n", mod_name); | |
e9d376f0 JB |
1035 | return ret; |
1036 | } | |
e9d376f0 JB |
1037 | |
1038 | static void ddebug_remove_all_tables(void) | |
1039 | { | |
1040 | mutex_lock(&ddebug_lock); | |
1041 | while (!list_empty(&ddebug_tables)) { | |
1042 | struct ddebug_table *dt = list_entry(ddebug_tables.next, | |
1043 | struct ddebug_table, | |
1044 | link); | |
1045 | ddebug_table_free(dt); | |
1046 | } | |
1047 | mutex_unlock(&ddebug_lock); | |
1048 | } | |
1049 | ||
6a5c083d TR |
1050 | static __initdata int ddebug_init_success; |
1051 | ||
239a5791 | 1052 | static int __init dynamic_debug_init_control(void) |
e9d376f0 | 1053 | { |
239a5791 GKH |
1054 | struct proc_dir_entry *procfs_dir; |
1055 | struct dentry *debugfs_dir; | |
6a5c083d TR |
1056 | |
1057 | if (!ddebug_init_success) | |
1058 | return -ENODEV; | |
e9d376f0 | 1059 | |
239a5791 GKH |
1060 | /* Create the control file in debugfs if it is enabled */ |
1061 | if (debugfs_initialized()) { | |
1062 | debugfs_dir = debugfs_create_dir("dynamic_debug", NULL); | |
1063 | debugfs_create_file("control", 0644, debugfs_dir, NULL, | |
1064 | &ddebug_proc_fops); | |
1065 | } | |
1066 | ||
1067 | /* Also create the control file in procfs */ | |
1068 | procfs_dir = proc_mkdir("dynamic_debug", NULL); | |
1069 | if (procfs_dir) | |
1070 | proc_create("control", 0644, procfs_dir, &proc_fops); | |
9fd714cd | 1071 | |
6a5c083d TR |
1072 | return 0; |
1073 | } | |
1074 | ||
1075 | static int __init dynamic_debug_init(void) | |
1076 | { | |
aa86a154 JC |
1077 | struct _ddebug *iter, *iter_mod_start; |
1078 | int ret, i, mod_sites, mod_ct; | |
1079 | const char *modname; | |
b48420c1 | 1080 | char *cmdline; |
6a5c083d | 1081 | |
b7b4eebd JC |
1082 | struct _ddebug_info di = { |
1083 | .descs = __start___dyndbg, | |
66f4006b | 1084 | .classes = __start___dyndbg_classes, |
b7b4eebd | 1085 | .num_descs = __stop___dyndbg - __start___dyndbg, |
66f4006b | 1086 | .num_classes = __stop___dyndbg_classes - __start___dyndbg_classes, |
b7b4eebd JC |
1087 | }; |
1088 | ||
e5ebffe1 | 1089 | if (&__start___dyndbg == &__stop___dyndbg) { |
ceabef7d OZ |
1090 | if (IS_ENABLED(CONFIG_DYNAMIC_DEBUG)) { |
1091 | pr_warn("_ddebug table is empty in a CONFIG_DYNAMIC_DEBUG build\n"); | |
1092 | return 1; | |
1093 | } | |
1094 | pr_info("Ignore empty _ddebug table in a CONFIG_DYNAMIC_DEBUG_CORE build\n"); | |
1095 | ddebug_init_success = 1; | |
1096 | return 0; | |
b5b78f83 | 1097 | } |
aa86a154 JC |
1098 | |
1099 | iter = iter_mod_start = __start___dyndbg; | |
b5b78f83 | 1100 | modname = iter->modname; |
aa86a154 JC |
1101 | i = mod_sites = mod_ct = 0; |
1102 | ||
1103 | for (; iter < __stop___dyndbg; iter++, i++, mod_sites++) { | |
1104 | ||
b5b78f83 | 1105 | if (strcmp(modname, iter->modname)) { |
aa86a154 | 1106 | mod_ct++; |
b7b4eebd JC |
1107 | di.num_descs = mod_sites; |
1108 | di.descs = iter_mod_start; | |
1109 | ret = __ddebug_add_module(&di, i - mod_sites, modname); | |
b5b78f83 | 1110 | if (ret) |
af442399 | 1111 | goto out_err; |
aa86a154 JC |
1112 | |
1113 | mod_sites = 0; | |
b5b78f83 | 1114 | modname = iter->modname; |
aa86a154 | 1115 | iter_mod_start = iter; |
e9d376f0 | 1116 | } |
e9d376f0 | 1117 | } |
b7b4eebd JC |
1118 | di.num_descs = mod_sites; |
1119 | di.descs = iter_mod_start; | |
1120 | ret = __ddebug_add_module(&di, i - mod_sites, modname); | |
b5b78f83 | 1121 | if (ret) |
af442399 | 1122 | goto out_err; |
a648ec05 | 1123 | |
af442399 | 1124 | ddebug_init_success = 1; |
7af56628 | 1125 | vpr_info("%d prdebugs in %d modules, %d KiB in ddebug tables, %d kiB in __dyndbg section\n", |
aa86a154 JC |
1126 | i, mod_ct, (int)((mod_ct * sizeof(struct ddebug_table)) >> 10), |
1127 | (int)((i * sizeof(struct _ddebug)) >> 10)); | |
af442399 | 1128 | |
66f4006b JC |
1129 | if (di.num_classes) |
1130 | v2pr_info(" %d builtin ddebug class-maps\n", di.num_classes); | |
1131 | ||
b48420c1 JC |
1132 | /* now that ddebug tables are loaded, process all boot args |
1133 | * again to find and activate queries given in dyndbg params. | |
1134 | * While this has already been done for known boot params, it | |
1135 | * ignored the unknown ones (dyndbg in particular). Reusing | |
1136 | * parse_args avoids ad-hoc parsing. This will also attempt | |
1137 | * to activate queries for not-yet-loaded modules, which is | |
1138 | * slightly noisy if verbose, but harmless. | |
1139 | */ | |
1140 | cmdline = kstrdup(saved_command_line, GFP_KERNEL); | |
1141 | parse_args("dyndbg params", cmdline, NULL, | |
ecc86170 | 1142 | 0, 0, 0, NULL, &ddebug_dyndbg_boot_param_cb); |
b48420c1 | 1143 | kfree(cmdline); |
af442399 | 1144 | return 0; |
a648ec05 | 1145 | |
af442399 JC |
1146 | out_err: |
1147 | ddebug_remove_all_tables(); | |
e9d376f0 JB |
1148 | return 0; |
1149 | } | |
6a5c083d | 1150 | /* Allow early initialization for boot messages via boot param */ |
3ec5652a | 1151 | early_initcall(dynamic_debug_init); |
b48420c1 | 1152 | |
6a5c083d | 1153 | /* Debugfs setup must be done later */ |
239a5791 | 1154 | fs_initcall(dynamic_debug_init_control); |