Commit 996fe061 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'kgdb-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/danielt/linux

Pull kgdb updates from Daniel Thompson:
 "Changes for kgdb/kdb this cycle are dominated by a change from Sumit
  that removes as small (256K) private heap from kdb. This is change
  I've hoped for ever since I discovered how few users of this heap
  remained in the kernel, so many thanks to Sumit for hunting these
  down.

  The other change is an incremental step towards SPDX headers"

* tag 'kgdb-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/danielt/linux:
  kernel: debug: Convert to SPDX identifier
  kdb: Rename members of struct kdbtab_t
  kdb: Simplify kdb_defcmd macro logic
  kdb: Get rid of redundant kdb_register_flags()
  kdb: Rename struct defcmd_set to struct kdb_macro
  kdb: Get rid of custom debug heap allocator
parents 0bcfe68b f8416aa2
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
* Copyright (C) 2009 Jason Wessel <jason.wessel@windriver.com> * Copyright (C) 2009 Jason Wessel <jason.wessel@windriver.com>
*/ */
#include <linux/list.h>
/* Shifted versions of the command enable bits are be used if the command /* Shifted versions of the command enable bits are be used if the command
* has no arguments (see kdb_check_flags). This allows commands, such as * has no arguments (see kdb_check_flags). This allows commands, such as
* go, to have different permissions depending upon whether it is called * go, to have different permissions depending upon whether it is called
...@@ -64,6 +66,17 @@ typedef enum { ...@@ -64,6 +66,17 @@ typedef enum {
typedef int (*kdb_func_t)(int, const char **); typedef int (*kdb_func_t)(int, const char **);
/* The KDB shell command table */
typedef struct _kdbtab {
char *name; /* Command name */
kdb_func_t func; /* Function to execute command */
char *usage; /* Usage String for this command */
char *help; /* Help message for this command */
short minlen; /* Minimum legal # cmd chars required */
kdb_cmdflags_t flags; /* Command behaviour flags */
struct list_head list_node; /* Command list */
} kdbtab_t;
#ifdef CONFIG_KGDB_KDB #ifdef CONFIG_KGDB_KDB
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -193,19 +206,13 @@ static inline const char *kdb_walk_kallsyms(loff_t *pos) ...@@ -193,19 +206,13 @@ static inline const char *kdb_walk_kallsyms(loff_t *pos)
#endif /* ! CONFIG_KALLSYMS */ #endif /* ! CONFIG_KALLSYMS */
/* Dynamic kdb shell command registration */ /* Dynamic kdb shell command registration */
extern int kdb_register(char *, kdb_func_t, char *, char *, short); extern int kdb_register(kdbtab_t *cmd);
extern int kdb_register_flags(char *, kdb_func_t, char *, char *, extern void kdb_unregister(kdbtab_t *cmd);
short, kdb_cmdflags_t);
extern int kdb_unregister(char *);
#else /* ! CONFIG_KGDB_KDB */ #else /* ! CONFIG_KGDB_KDB */
static inline __printf(1, 2) int kdb_printf(const char *fmt, ...) { return 0; } static inline __printf(1, 2) int kdb_printf(const char *fmt, ...) { return 0; }
static inline void kdb_init(int level) {} static inline void kdb_init(int level) {}
static inline int kdb_register(char *cmd, kdb_func_t func, char *usage, static inline int kdb_register(kdbtab_t *cmd) { return 0; }
char *help, short minlen) { return 0; } static inline void kdb_unregister(kdbtab_t *cmd) {}
static inline int kdb_register_flags(char *cmd, kdb_func_t func, char *usage,
char *help, short minlen,
kdb_cmdflags_t flags) { return 0; }
static inline int kdb_unregister(char *cmd) { return 0; }
#endif /* CONFIG_KGDB_KDB */ #endif /* CONFIG_KGDB_KDB */
enum { enum {
KDB_NOT_INITIALIZED, KDB_NOT_INITIALIZED,
......
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Kernel Debug Core * Kernel Debug Core
* *
...@@ -22,10 +23,6 @@ ...@@ -22,10 +23,6 @@
* *
* Original KGDB stub: David Grothe <dave@gcom.com>, * Original KGDB stub: David Grothe <dave@gcom.com>,
* Tigran Aivazian <tigran@sco.com> * Tigran Aivazian <tigran@sco.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/ */
#define pr_fmt(fmt) "KGDB: " fmt #define pr_fmt(fmt) "KGDB: " fmt
......
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Kernel Debug Core * Kernel Debug Core
* *
...@@ -22,10 +23,6 @@ ...@@ -22,10 +23,6 @@
* *
* Original KGDB stub: David Grothe <dave@gcom.com>, * Original KGDB stub: David Grothe <dave@gcom.com>,
* Tigran Aivazian <tigran@sco.com> * Tigran Aivazian <tigran@sco.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
......
...@@ -523,51 +523,51 @@ static int kdb_ss(int argc, const char **argv) ...@@ -523,51 +523,51 @@ static int kdb_ss(int argc, const char **argv)
} }
static kdbtab_t bptab[] = { static kdbtab_t bptab[] = {
{ .cmd_name = "bp", { .name = "bp",
.cmd_func = kdb_bp, .func = kdb_bp,
.cmd_usage = "[<vaddr>]", .usage = "[<vaddr>]",
.cmd_help = "Set/Display breakpoints", .help = "Set/Display breakpoints",
.cmd_flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS, .flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS,
}, },
{ .cmd_name = "bl", { .name = "bl",
.cmd_func = kdb_bp, .func = kdb_bp,
.cmd_usage = "[<vaddr>]", .usage = "[<vaddr>]",
.cmd_help = "Display breakpoints", .help = "Display breakpoints",
.cmd_flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS, .flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS,
}, },
{ .cmd_name = "bc", { .name = "bc",
.cmd_func = kdb_bc, .func = kdb_bc,
.cmd_usage = "<bpnum>", .usage = "<bpnum>",
.cmd_help = "Clear Breakpoint", .help = "Clear Breakpoint",
.cmd_flags = KDB_ENABLE_FLOW_CTRL, .flags = KDB_ENABLE_FLOW_CTRL,
}, },
{ .cmd_name = "be", { .name = "be",
.cmd_func = kdb_bc, .func = kdb_bc,
.cmd_usage = "<bpnum>", .usage = "<bpnum>",
.cmd_help = "Enable Breakpoint", .help = "Enable Breakpoint",
.cmd_flags = KDB_ENABLE_FLOW_CTRL, .flags = KDB_ENABLE_FLOW_CTRL,
}, },
{ .cmd_name = "bd", { .name = "bd",
.cmd_func = kdb_bc, .func = kdb_bc,
.cmd_usage = "<bpnum>", .usage = "<bpnum>",
.cmd_help = "Disable Breakpoint", .help = "Disable Breakpoint",
.cmd_flags = KDB_ENABLE_FLOW_CTRL, .flags = KDB_ENABLE_FLOW_CTRL,
}, },
{ .cmd_name = "ss", { .name = "ss",
.cmd_func = kdb_ss, .func = kdb_ss,
.cmd_usage = "", .usage = "",
.cmd_help = "Single Step", .help = "Single Step",
.cmd_minlen = 1, .minlen = 1,
.cmd_flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS, .flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS,
}, },
}; };
static kdbtab_t bphcmd = { static kdbtab_t bphcmd = {
.cmd_name = "bph", .name = "bph",
.cmd_func = kdb_bp, .func = kdb_bp,
.cmd_usage = "[<vaddr>]", .usage = "[<vaddr>]",
.cmd_help = "[datar [length]|dataw [length]] Set hw brk", .help = "[datar [length]|dataw [length]] Set hw brk",
.cmd_flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS, .flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS,
}; };
/* Initialize the breakpoint table and register breakpoint commands. */ /* Initialize the breakpoint table and register breakpoint commands. */
......
...@@ -140,7 +140,6 @@ int kdb_stub(struct kgdb_state *ks) ...@@ -140,7 +140,6 @@ int kdb_stub(struct kgdb_state *ks)
*/ */
kdb_common_deinit_state(); kdb_common_deinit_state();
KDB_STATE_CLEAR(PAGER); KDB_STATE_CLEAR(PAGER);
kdbnearsym_cleanup();
if (error == KDB_CMD_KGDB) { if (error == KDB_CMD_KGDB) {
if (KDB_STATE(DOING_KGDB)) if (KDB_STATE(DOING_KGDB))
KDB_STATE_CLEAR(DOING_KGDB); KDB_STATE_CLEAR(DOING_KGDB);
......
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/kgdb.h> #include <linux/kgdb.h>
#include <linux/kdb.h> #include <linux/kdb.h>
#include <linux/list.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -654,16 +653,17 @@ static void kdb_cmderror(int diag) ...@@ -654,16 +653,17 @@ static void kdb_cmderror(int diag)
* Returns: * Returns:
* zero for success, a kdb diagnostic if error * zero for success, a kdb diagnostic if error
*/ */
struct defcmd_set { struct kdb_macro {
int count; kdbtab_t cmd; /* Macro command */
bool usable; struct list_head statements; /* Associated statement list */
char *name;
char *usage;
char *help;
char **command;
}; };
static struct defcmd_set *defcmd_set;
static int defcmd_set_count; struct kdb_macro_statement {
char *statement; /* Statement text */
struct list_head list_node; /* Statement list node */
};
static struct kdb_macro *kdb_macro;
static bool defcmd_in_progress; static bool defcmd_in_progress;
/* Forward references */ /* Forward references */
...@@ -671,54 +671,56 @@ static int kdb_exec_defcmd(int argc, const char **argv); ...@@ -671,54 +671,56 @@ static int kdb_exec_defcmd(int argc, const char **argv);
static int kdb_defcmd2(const char *cmdstr, const char *argv0) static int kdb_defcmd2(const char *cmdstr, const char *argv0)
{ {
struct defcmd_set *s = defcmd_set + defcmd_set_count - 1; struct kdb_macro_statement *kms;
char **save_command = s->command;
if (!kdb_macro)
return KDB_NOTIMP;
if (strcmp(argv0, "endefcmd") == 0) { if (strcmp(argv0, "endefcmd") == 0) {
defcmd_in_progress = false; defcmd_in_progress = false;
if (!s->count) if (!list_empty(&kdb_macro->statements))
s->usable = false; kdb_register(&kdb_macro->cmd);
if (s->usable)
/* macros are always safe because when executed each
* internal command re-enters kdb_parse() and is
* safety checked individually.
*/
kdb_register_flags(s->name, kdb_exec_defcmd, s->usage,
s->help, 0,
KDB_ENABLE_ALWAYS_SAFE);
return 0; return 0;
} }
if (!s->usable)
return KDB_NOTIMP; kms = kmalloc(sizeof(*kms), GFP_KDB);
s->command = kcalloc(s->count + 1, sizeof(*(s->command)), GFP_KDB); if (!kms) {
if (!s->command) { kdb_printf("Could not allocate new kdb macro command: %s\n",
kdb_printf("Could not allocate new kdb_defcmd table for %s\n",
cmdstr); cmdstr);
s->usable = false;
return KDB_NOTIMP; return KDB_NOTIMP;
} }
memcpy(s->command, save_command, s->count * sizeof(*(s->command)));
s->command[s->count++] = kdb_strdup(cmdstr, GFP_KDB); kms->statement = kdb_strdup(cmdstr, GFP_KDB);
kfree(save_command); list_add_tail(&kms->list_node, &kdb_macro->statements);
return 0; return 0;
} }
static int kdb_defcmd(int argc, const char **argv) static int kdb_defcmd(int argc, const char **argv)
{ {
struct defcmd_set *save_defcmd_set = defcmd_set, *s; kdbtab_t *mp;
if (defcmd_in_progress) { if (defcmd_in_progress) {
kdb_printf("kdb: nested defcmd detected, assuming missing " kdb_printf("kdb: nested defcmd detected, assuming missing "
"endefcmd\n"); "endefcmd\n");
kdb_defcmd2("endefcmd", "endefcmd"); kdb_defcmd2("endefcmd", "endefcmd");
} }
if (argc == 0) { if (argc == 0) {
int i; kdbtab_t *kp;
for (s = defcmd_set; s < defcmd_set + defcmd_set_count; ++s) { struct kdb_macro *kmp;
kdb_printf("defcmd %s \"%s\" \"%s\"\n", s->name, struct kdb_macro_statement *kms;
s->usage, s->help);
for (i = 0; i < s->count; ++i) list_for_each_entry(kp, &kdb_cmds_head, list_node) {
kdb_printf("%s", s->command[i]); if (kp->func == kdb_exec_defcmd) {
kdb_printf("defcmd %s \"%s\" \"%s\"\n",
kp->name, kp->usage, kp->help);
kmp = container_of(kp, struct kdb_macro, cmd);
list_for_each_entry(kms, &kmp->statements,
list_node)
kdb_printf("%s", kms->statement);
kdb_printf("endefcmd\n"); kdb_printf("endefcmd\n");
} }
}
return 0; return 0;
} }
if (argc != 3) if (argc != 3)
...@@ -727,45 +729,43 @@ static int kdb_defcmd(int argc, const char **argv) ...@@ -727,45 +729,43 @@ static int kdb_defcmd(int argc, const char **argv)
kdb_printf("Command only available during kdb_init()\n"); kdb_printf("Command only available during kdb_init()\n");
return KDB_NOTIMP; return KDB_NOTIMP;
} }
defcmd_set = kmalloc_array(defcmd_set_count + 1, sizeof(*defcmd_set), kdb_macro = kzalloc(sizeof(*kdb_macro), GFP_KDB);
GFP_KDB); if (!kdb_macro)
if (!defcmd_set)
goto fail_defcmd; goto fail_defcmd;
memcpy(defcmd_set, save_defcmd_set,
defcmd_set_count * sizeof(*defcmd_set)); mp = &kdb_macro->cmd;
s = defcmd_set + defcmd_set_count; mp->func = kdb_exec_defcmd;
memset(s, 0, sizeof(*s)); mp->minlen = 0;
s->usable = true; mp->flags = KDB_ENABLE_ALWAYS_SAFE;
s->name = kdb_strdup(argv[1], GFP_KDB); mp->name = kdb_strdup(argv[1], GFP_KDB);
if (!s->name) if (!mp->name)
goto fail_name; goto fail_name;
s->usage = kdb_strdup(argv[2], GFP_KDB); mp->usage = kdb_strdup(argv[2], GFP_KDB);
if (!s->usage) if (!mp->usage)
goto fail_usage; goto fail_usage;
s->help = kdb_strdup(argv[3], GFP_KDB); mp->help = kdb_strdup(argv[3], GFP_KDB);
if (!s->help) if (!mp->help)
goto fail_help; goto fail_help;
if (s->usage[0] == '"') { if (mp->usage[0] == '"') {
strcpy(s->usage, argv[2]+1); strcpy(mp->usage, argv[2]+1);
s->usage[strlen(s->usage)-1] = '\0'; mp->usage[strlen(mp->usage)-1] = '\0';
} }
if (s->help[0] == '"') { if (mp->help[0] == '"') {
strcpy(s->help, argv[3]+1); strcpy(mp->help, argv[3]+1);
s->help[strlen(s->help)-1] = '\0'; mp->help[strlen(mp->help)-1] = '\0';
} }
++defcmd_set_count;
INIT_LIST_HEAD(&kdb_macro->statements);
defcmd_in_progress = true; defcmd_in_progress = true;
kfree(save_defcmd_set);
return 0; return 0;
fail_help: fail_help:
kfree(s->usage); kfree(mp->usage);
fail_usage: fail_usage:
kfree(s->name); kfree(mp->name);
fail_name: fail_name:
kfree(defcmd_set); kfree(kdb_macro);
fail_defcmd: fail_defcmd:
kdb_printf("Could not allocate new defcmd_set entry for %s\n", argv[1]); kdb_printf("Could not allocate new kdb_macro entry for %s\n", argv[1]);
defcmd_set = save_defcmd_set;
return KDB_NOTIMP; return KDB_NOTIMP;
} }
...@@ -780,25 +780,31 @@ static int kdb_defcmd(int argc, const char **argv) ...@@ -780,25 +780,31 @@ static int kdb_defcmd(int argc, const char **argv)
*/ */
static int kdb_exec_defcmd(int argc, const char **argv) static int kdb_exec_defcmd(int argc, const char **argv)
{ {
int i, ret; int ret;
struct defcmd_set *s; kdbtab_t *kp;
struct kdb_macro *kmp;
struct kdb_macro_statement *kms;
if (argc != 0) if (argc != 0)
return KDB_ARGCOUNT; return KDB_ARGCOUNT;
for (s = defcmd_set, i = 0; i < defcmd_set_count; ++i, ++s) {
if (strcmp(s->name, argv[0]) == 0) list_for_each_entry(kp, &kdb_cmds_head, list_node) {
if (strcmp(kp->name, argv[0]) == 0)
break; break;
} }
if (i == defcmd_set_count) { if (list_entry_is_head(kp, &kdb_cmds_head, list_node)) {
kdb_printf("kdb_exec_defcmd: could not find commands for %s\n", kdb_printf("kdb_exec_defcmd: could not find commands for %s\n",
argv[0]); argv[0]);
return KDB_NOTIMP; return KDB_NOTIMP;
} }
for (i = 0; i < s->count; ++i) { kmp = container_of(kp, struct kdb_macro, cmd);
/* Recursive use of kdb_parse, do not use argv after list_for_each_entry(kms, &kmp->statements, list_node) {
* this point */ /*
* Recursive use of kdb_parse, do not use argv after this point.
*/
argv = NULL; argv = NULL;
kdb_printf("[%s]kdb> %s\n", s->name, s->command[i]); kdb_printf("[%s]kdb> %s\n", kmp->cmd.name, kms->statement);
ret = kdb_parse(s->command[i]); ret = kdb_parse(kms->statement);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -1009,11 +1015,11 @@ int kdb_parse(const char *cmdstr) ...@@ -1009,11 +1015,11 @@ int kdb_parse(const char *cmdstr)
* If this command is allowed to be abbreviated, * If this command is allowed to be abbreviated,
* check to see if this is it. * check to see if this is it.
*/ */
if (tp->cmd_minlen && (strlen(argv[0]) <= tp->cmd_minlen) && if (tp->minlen && (strlen(argv[0]) <= tp->minlen) &&
(strncmp(argv[0], tp->cmd_name, tp->cmd_minlen) == 0)) (strncmp(argv[0], tp->name, tp->minlen) == 0))
break; break;
if (strcmp(argv[0], tp->cmd_name) == 0) if (strcmp(argv[0], tp->name) == 0)
break; break;
} }
...@@ -1024,8 +1030,7 @@ int kdb_parse(const char *cmdstr) ...@@ -1024,8 +1030,7 @@ int kdb_parse(const char *cmdstr)
*/ */
if (list_entry_is_head(tp, &kdb_cmds_head, list_node)) { if (list_entry_is_head(tp, &kdb_cmds_head, list_node)) {
list_for_each_entry(tp, &kdb_cmds_head, list_node) { list_for_each_entry(tp, &kdb_cmds_head, list_node) {
if (strncmp(argv[0], tp->cmd_name, if (strncmp(argv[0], tp->name, strlen(tp->name)) == 0)
strlen(tp->cmd_name)) == 0)
break; break;
} }
} }
...@@ -1033,19 +1038,19 @@ int kdb_parse(const char *cmdstr) ...@@ -1033,19 +1038,19 @@ int kdb_parse(const char *cmdstr)
if (!list_entry_is_head(tp, &kdb_cmds_head, list_node)) { if (!list_entry_is_head(tp, &kdb_cmds_head, list_node)) {
int result; int result;
if (!kdb_check_flags(tp->cmd_flags, kdb_cmd_enabled, argc <= 1)) if (!kdb_check_flags(tp->flags, kdb_cmd_enabled, argc <= 1))
return KDB_NOPERM; return KDB_NOPERM;
KDB_STATE_SET(CMD); KDB_STATE_SET(CMD);
result = (*tp->cmd_func)(argc-1, (const char **)argv); result = (*tp->func)(argc-1, (const char **)argv);
if (result && ignore_errors && result > KDB_CMD_GO) if (result && ignore_errors && result > KDB_CMD_GO)
result = 0; result = 0;
KDB_STATE_CLEAR(CMD); KDB_STATE_CLEAR(CMD);
if (tp->cmd_flags & KDB_REPEAT_WITH_ARGS) if (tp->flags & KDB_REPEAT_WITH_ARGS)
return result; return result;
argc = tp->cmd_flags & KDB_REPEAT_NO_ARGS ? 1 : 0; argc = tp->flags & KDB_REPEAT_NO_ARGS ? 1 : 0;
if (argv[argc]) if (argv[argc])
*(argv[argc]) = '\0'; *(argv[argc]) = '\0';
return result; return result;
...@@ -2412,12 +2417,12 @@ static int kdb_help(int argc, const char **argv) ...@@ -2412,12 +2417,12 @@ static int kdb_help(int argc, const char **argv)
char *space = ""; char *space = "";
if (KDB_FLAG(CMD_INTERRUPT)) if (KDB_FLAG(CMD_INTERRUPT))
return 0; return 0;
if (!kdb_check_flags(kt->cmd_flags, kdb_cmd_enabled, true)) if (!kdb_check_flags(kt->flags, kdb_cmd_enabled, true))
continue; continue;
if (strlen(kt->cmd_usage) > 20) if (strlen(kt->usage) > 20)
space = "\n "; space = "\n ";
kdb_printf("%-15.15s %-20s%s%s\n", kt->cmd_name, kdb_printf("%-15.15s %-20s%s%s\n", kt->name,
kt->cmd_usage, space, kt->cmd_help); kt->usage, space, kt->help);
} }
return 0; return 0;
} }
...@@ -2613,56 +2618,32 @@ static int kdb_grep_help(int argc, const char **argv) ...@@ -2613,56 +2618,32 @@ static int kdb_grep_help(int argc, const char **argv)
return 0; return 0;
} }
/* /**
* kdb_register_flags - This function is used to register a kernel * kdb_register() - This function is used to register a kernel debugger
* debugger command. * command.
* Inputs: * @cmd: pointer to kdb command
* cmd Command name *
* func Function to execute the command * Note that it's the job of the caller to keep the memory for the cmd
* usage A simple usage string showing arguments * allocated until unregister is called.
* help A simple help string describing command */
* repeat Does the command auto repeat on enter? int kdb_register(kdbtab_t *cmd)
* Returns:
* zero for success, one if a duplicate command.
*/
int kdb_register_flags(char *cmd,
kdb_func_t func,
char *usage,
char *help,
short minlen,
kdb_cmdflags_t flags)
{ {
kdbtab_t *kp; kdbtab_t *kp;
list_for_each_entry(kp, &kdb_cmds_head, list_node) { list_for_each_entry(kp, &kdb_cmds_head, list_node) {
if (strcmp(kp->cmd_name, cmd) == 0) { if (strcmp(kp->name, cmd->name) == 0) {
kdb_printf("Duplicate kdb command registered: " kdb_printf("Duplicate kdb cmd: %s, func %p help %s\n",
"%s, func %px help %s\n", cmd, func, help); cmd->name, cmd->func, cmd->help);
return 1; return 1;
} }
} }
kp = kmalloc(sizeof(*kp), GFP_KDB); list_add_tail(&cmd->list_node, &kdb_cmds_head);
if (!kp) {
kdb_printf("Could not allocate new kdb_command table\n");
return 1;
}
kp->cmd_name = cmd;
kp->cmd_func = func;
kp->cmd_usage = usage;
kp->cmd_help = help;
kp->cmd_minlen = minlen;
kp->cmd_flags = flags;
kp->is_dynamic = true;
list_add_tail(&kp->list_node, &kdb_cmds_head);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(kdb_register_flags); EXPORT_SYMBOL_GPL(kdb_register);
/* /**
* kdb_register_table() - This function is used to register a kdb command * kdb_register_table() - This function is used to register a kdb command
* table. * table.
* @kp: pointer to kdb command table * @kp: pointer to kdb command table
...@@ -2676,266 +2657,231 @@ void kdb_register_table(kdbtab_t *kp, size_t len) ...@@ -2676,266 +2657,231 @@ void kdb_register_table(kdbtab_t *kp, size_t len)
} }
} }
/* /**
* kdb_register - Compatibility register function for commands that do * kdb_unregister() - This function is used to unregister a kernel debugger
* not need to specify a repeat state. Equivalent to * command. It is generally called when a module which
* kdb_register_flags with flags set to 0. * implements kdb command is unloaded.
* Inputs: * @cmd: pointer to kdb command
* cmd Command name
* func Function to execute the command
* usage A simple usage string showing arguments
* help A simple help string describing command
* Returns:
* zero for success, one if a duplicate command.
*/
int kdb_register(char *cmd,
kdb_func_t func,
char *usage,
char *help,
short minlen)
{
return kdb_register_flags(cmd, func, usage, help, minlen, 0);
}
EXPORT_SYMBOL_GPL(kdb_register);
/*
* kdb_unregister - This function is used to unregister a kernel
* debugger command. It is generally called when a module which
* implements kdb commands is unloaded.
* Inputs:
* cmd Command name
* Returns:
* zero for success, one command not registered.
*/ */
int kdb_unregister(char *cmd) void kdb_unregister(kdbtab_t *cmd)
{ {
kdbtab_t *kp; list_del(&cmd->list_node);
/*
* find the command.
*/
list_for_each_entry(kp, &kdb_cmds_head, list_node) {
if (strcmp(kp->cmd_name, cmd) == 0) {
list_del(&kp->list_node);
if (kp->is_dynamic)
kfree(kp);
return 0;
}
}
/* Couldn't find it. */
return 1;
} }
EXPORT_SYMBOL_GPL(kdb_unregister); EXPORT_SYMBOL_GPL(kdb_unregister);
static kdbtab_t maintab[] = { static kdbtab_t maintab[] = {
{ .cmd_name = "md", { .name = "md",
.cmd_func = kdb_md, .func = kdb_md,
.cmd_usage = "<vaddr>", .usage = "<vaddr>",
.cmd_help = "Display Memory Contents, also mdWcN, e.g. md8c1", .help = "Display Memory Contents, also mdWcN, e.g. md8c1",
.cmd_minlen = 1, .minlen = 1,
.cmd_flags = KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS, .flags = KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS,
}, },
{ .cmd_name = "mdr", { .name = "mdr",
.cmd_func = kdb_md, .func = kdb_md,
.cmd_usage = "<vaddr> <bytes>", .usage = "<vaddr> <bytes>",
.cmd_help = "Display Raw Memory", .help = "Display Raw Memory",
.cmd_flags = KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS, .flags = KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS,
}, },
{ .cmd_name = "mdp", { .name = "mdp",
.cmd_func = kdb_md, .func = kdb_md,
.cmd_usage = "<paddr> <bytes>", .usage = "<paddr> <bytes>",
.cmd_help = "Display Physical Memory", .help = "Display Physical Memory",
.cmd_flags = KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS, .flags = KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS,
}, },
{ .cmd_name = "mds", { .name = "mds",
.cmd_func = kdb_md, .func = kdb_md,
.cmd_usage = "<vaddr>", .usage = "<vaddr>",
.cmd_help = "Display Memory Symbolically", .help = "Display Memory Symbolically",
.cmd_flags = KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS, .flags = KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS,
}, },
{ .cmd_name = "mm", { .name = "mm",
.cmd_func = kdb_mm, .func = kdb_mm,
.cmd_usage = "<vaddr> <contents>", .usage = "<vaddr> <contents>",
.cmd_help = "Modify Memory Contents", .help = "Modify Memory Contents",
.cmd_flags = KDB_ENABLE_MEM_WRITE | KDB_REPEAT_NO_ARGS, .flags = KDB_ENABLE_MEM_WRITE | KDB_REPEAT_NO_ARGS,
}, },
{ .cmd_name = "go", { .name = "go",
.cmd_func = kdb_go, .func = kdb_go,
.cmd_usage = "[<vaddr>]", .usage = "[<vaddr>]",
.cmd_help = "Continue Execution", .help = "Continue Execution",
.cmd_minlen = 1, .minlen = 1,
.cmd_flags = KDB_ENABLE_REG_WRITE | .flags = KDB_ENABLE_REG_WRITE |
KDB_ENABLE_ALWAYS_SAFE_NO_ARGS, KDB_ENABLE_ALWAYS_SAFE_NO_ARGS,
}, },
{ .cmd_name = "rd", { .name = "rd",
.cmd_func = kdb_rd, .func = kdb_rd,
.cmd_usage = "", .usage = "",
.cmd_help = "Display Registers", .help = "Display Registers",
.cmd_flags = KDB_ENABLE_REG_READ, .flags = KDB_ENABLE_REG_READ,
}, },
{ .cmd_name = "rm", { .name = "rm",
.cmd_func = kdb_rm, .func = kdb_rm,
.cmd_usage = "<reg> <contents>", .usage = "<reg> <contents>",
.cmd_help = "Modify Registers", .help = "Modify Registers",
.cmd_flags = KDB_ENABLE_REG_WRITE, .flags = KDB_ENABLE_REG_WRITE,
}, },
{ .cmd_name = "ef", { .name = "ef",
.cmd_func = kdb_ef, .func = kdb_ef,
.cmd_usage = "<vaddr>", .usage = "<vaddr>",
.cmd_help = "Display exception frame", .help = "Display exception frame",
.cmd_flags = KDB_ENABLE_MEM_READ, .flags = KDB_ENABLE_MEM_READ,
}, },
{ .cmd_name = "bt", { .name = "bt",
.cmd_func = kdb_bt, .func = kdb_bt,
.cmd_usage = "[<vaddr>]", .usage = "[<vaddr>]",
.cmd_help = "Stack traceback", .help = "Stack traceback",
.cmd_minlen = 1, .minlen = 1,
.cmd_flags = KDB_ENABLE_MEM_READ | KDB_ENABLE_INSPECT_NO_ARGS, .flags = KDB_ENABLE_MEM_READ | KDB_ENABLE_INSPECT_NO_ARGS,
}, },
{ .cmd_name = "btp", { .name = "btp",
.cmd_func = kdb_bt, .func = kdb_bt,
.cmd_usage = "<pid>", .usage = "<pid>",
.cmd_help = "Display stack for process <pid>", .help = "Display stack for process <pid>",
.cmd_flags = KDB_ENABLE_INSPECT, .flags = KDB_ENABLE_INSPECT,
}, },
{ .cmd_name = "bta", { .name = "bta",
.cmd_func = kdb_bt, .func = kdb_bt,
.cmd_usage = "[D|R|S|T|C|Z|E|U|I|M|A]", .usage = "[D|R|S|T|C|Z|E|U|I|M|A]",
.cmd_help = "Backtrace all processes matching state flag", .help = "Backtrace all processes matching state flag",
.cmd_flags = KDB_ENABLE_INSPECT, .flags = KDB_ENABLE_INSPECT,
}, },
{ .cmd_name = "btc", { .name = "btc",
.cmd_func = kdb_bt, .func = kdb_bt,
.cmd_usage = "", .usage = "",
.cmd_help = "Backtrace current process on each cpu", .help = "Backtrace current process on each cpu",
.cmd_flags = KDB_ENABLE_INSPECT, .flags = KDB_ENABLE_INSPECT,
}, },
{ .cmd_name = "btt", { .name = "btt",
.cmd_func = kdb_bt, .func = kdb_bt,
.cmd_usage = "<vaddr>", .usage = "<vaddr>",
.cmd_help = "Backtrace process given its struct task address", .help = "Backtrace process given its struct task address",
.cmd_flags = KDB_ENABLE_MEM_READ | KDB_ENABLE_INSPECT_NO_ARGS, .flags = KDB_ENABLE_MEM_READ | KDB_ENABLE_INSPECT_NO_ARGS,
}, },
{ .cmd_name = "env", { .name = "env",
.cmd_func = kdb_env, .func = kdb_env,
.cmd_usage = "", .usage = "",
.cmd_help = "Show environment variables", .help = "Show environment variables",
.cmd_flags = KDB_ENABLE_ALWAYS_SAFE, .flags = KDB_ENABLE_ALWAYS_SAFE,
}, },
{ .cmd_name = "set", { .name = "set",
.cmd_func = kdb_set, .func = kdb_set,
.cmd_usage = "", .usage = "",
.cmd_help = "Set environment variables", .help = "Set environment variables",
.cmd_flags = KDB_ENABLE_ALWAYS_SAFE, .flags = KDB_ENABLE_ALWAYS_SAFE,
}, },
{ .cmd_name = "help", { .name = "help",
.cmd_func = kdb_help, .func = kdb_help,
.cmd_usage = "", .usage = "",
.cmd_help = "Display Help Message", .help = "Display Help Message",
.cmd_minlen = 1, .minlen = 1,
.cmd_flags = KDB_ENABLE_ALWAYS_SAFE, .flags = KDB_ENABLE_ALWAYS_SAFE,
}, },
{ .cmd_name = "?", { .name = "?",
.cmd_func = kdb_help, .func = kdb_help,
.cmd_usage = "", .usage = "",
.cmd_help = "Display Help Message", .help = "Display Help Message",
.cmd_flags = KDB_ENABLE_ALWAYS_SAFE, .flags = KDB_ENABLE_ALWAYS_SAFE,
}, },
{ .cmd_name = "cpu", { .name = "cpu",
.cmd_func = kdb_cpu, .func = kdb_cpu,
.cmd_usage = "<cpunum>", .usage = "<cpunum>",
.cmd_help = "Switch to new cpu", .help = "Switch to new cpu",
.cmd_flags = KDB_ENABLE_ALWAYS_SAFE_NO_ARGS, .flags = KDB_ENABLE_ALWAYS_SAFE_NO_ARGS,
}, },
{ .cmd_name = "kgdb", { .name = "kgdb",
.cmd_func = kdb_kgdb, .func = kdb_kgdb,
.cmd_usage = "", .usage = "",
.cmd_help = "Enter kgdb mode", .help = "Enter kgdb mode",
.cmd_flags = 0, .flags = 0,
}, },
{ .cmd_name = "ps", { .name = "ps",
.cmd_func = kdb_ps, .func = kdb_ps,
.cmd_usage = "[<flags>|A]", .usage = "[<flags>|A]",
.cmd_help = "Display active task list", .help = "Display active task list",
.cmd_flags = KDB_ENABLE_INSPECT, .flags = KDB_ENABLE_INSPECT,
}, },
{ .cmd_name = "pid", { .name = "pid",
.cmd_func = kdb_pid, .func = kdb_pid,
.cmd_usage = "<pidnum>", .usage = "<pidnum>",
.cmd_help = "Switch to another task", .help = "Switch to another task",
.cmd_flags = KDB_ENABLE_INSPECT, .flags = KDB_ENABLE_INSPECT,
}, },
{ .cmd_name = "reboot", { .name = "reboot",
.cmd_func = kdb_reboot, .func = kdb_reboot,
.cmd_usage = "", .usage = "",
.cmd_help = "Reboot the machine immediately", .help = "Reboot the machine immediately",
.cmd_flags = KDB_ENABLE_REBOOT, .flags = KDB_ENABLE_REBOOT,
}, },
#if defined(CONFIG_MODULES) #if defined(CONFIG_MODULES)
{ .cmd_name = "lsmod", { .name = "lsmod",
.cmd_func = kdb_lsmod, .func = kdb_lsmod,
.cmd_usage = "", .usage = "",
.cmd_help = "List loaded kernel modules", .help = "List loaded kernel modules",
.cmd_flags = KDB_ENABLE_INSPECT, .flags = KDB_ENABLE_INSPECT,
}, },
#endif #endif
#if defined(CONFIG_MAGIC_SYSRQ) #if defined(CONFIG_MAGIC_SYSRQ)
{ .cmd_name = "sr", { .name = "sr",
.cmd_func = kdb_sr, .func = kdb_sr,
.cmd_usage = "<key>", .usage = "<key>",
.cmd_help = "Magic SysRq key", .help = "Magic SysRq key",
.cmd_flags = KDB_ENABLE_ALWAYS_SAFE, .flags = KDB_ENABLE_ALWAYS_SAFE,
}, },
#endif #endif
#if defined(CONFIG_PRINTK) #if defined(CONFIG_PRINTK)
{ .cmd_name = "dmesg", { .name = "dmesg",
.cmd_func = kdb_dmesg, .func = kdb_dmesg,
.cmd_usage = "[lines]", .usage = "[lines]",
.cmd_help = "Display syslog buffer", .help = "Display syslog buffer",
.cmd_flags = KDB_ENABLE_ALWAYS_SAFE, .flags = KDB_ENABLE_ALWAYS_SAFE,
}, },
#endif #endif
{ .cmd_name = "defcmd", { .name = "defcmd",
.cmd_func = kdb_defcmd, .func = kdb_defcmd,
.cmd_usage = "name \"usage\" \"help\"", .usage = "name \"usage\" \"help\"",
.cmd_help = "Define a set of commands, down to endefcmd", .help = "Define a set of commands, down to endefcmd",
.cmd_flags = KDB_ENABLE_ALWAYS_SAFE, /*
* Macros are always safe because when executed each
* internal command re-enters kdb_parse() and is safety
* checked individually.
*/
.flags = KDB_ENABLE_ALWAYS_SAFE,
}, },
{ .cmd_name = "kill", { .name = "kill",
.cmd_func = kdb_kill, .func = kdb_kill,
.cmd_usage = "<-signal> <pid>", .usage = "<-signal> <pid>",
.cmd_help = "Send a signal to a process", .help = "Send a signal to a process",
.cmd_flags = KDB_ENABLE_SIGNAL, .flags = KDB_ENABLE_SIGNAL,
}, },
{ .cmd_name = "summary", { .name = "summary",
.cmd_func = kdb_summary, .func = kdb_summary,
.cmd_usage = "", .usage = "",
.cmd_help = "Summarize the system", .help = "Summarize the system",
.cmd_minlen = 4, .minlen = 4,
.cmd_flags = KDB_ENABLE_ALWAYS_SAFE, .flags = KDB_ENABLE_ALWAYS_SAFE,
}, },
{ .cmd_name = "per_cpu", { .name = "per_cpu",
.cmd_func = kdb_per_cpu, .func = kdb_per_cpu,
.cmd_usage = "<sym> [<bytes>] [<cpu>]", .usage = "<sym> [<bytes>] [<cpu>]",
.cmd_help = "Display per_cpu variables", .help = "Display per_cpu variables",
.cmd_minlen = 3, .minlen = 3,
.cmd_flags = KDB_ENABLE_MEM_READ, .flags = KDB_ENABLE_MEM_READ,
}, },
{ .cmd_name = "grephelp", { .name = "grephelp",
.cmd_func = kdb_grep_help, .func = kdb_grep_help,
.cmd_usage = "", .usage = "",
.cmd_help = "Display help on | grep", .help = "Display help on | grep",
.cmd_flags = KDB_ENABLE_ALWAYS_SAFE, .flags = KDB_ENABLE_ALWAYS_SAFE,
}, },
}; };
static kdbtab_t nmicmd = { static kdbtab_t nmicmd = {
.cmd_name = "disable_nmi", .name = "disable_nmi",
.cmd_func = kdb_disable_nmi, .func = kdb_disable_nmi,
.cmd_usage = "", .usage = "",
.cmd_help = "Disable NMI entry to KDB", .help = "Disable NMI entry to KDB",
.cmd_flags = KDB_ENABLE_ALWAYS_SAFE, .flags = KDB_ENABLE_ALWAYS_SAFE,
}; };
/* Initialize the kdb command table. */ /* Initialize the kdb command table. */
......
...@@ -109,7 +109,6 @@ extern int kdbgetaddrarg(int, const char **, int*, unsigned long *, ...@@ -109,7 +109,6 @@ extern int kdbgetaddrarg(int, const char **, int*, unsigned long *,
long *, char **); long *, char **);
extern int kdbgetsymval(const char *, kdb_symtab_t *); extern int kdbgetsymval(const char *, kdb_symtab_t *);
extern int kdbnearsym(unsigned long, kdb_symtab_t *); extern int kdbnearsym(unsigned long, kdb_symtab_t *);
extern void kdbnearsym_cleanup(void);
extern char *kdb_strdup(const char *str, gfp_t type); extern char *kdb_strdup(const char *str, gfp_t type);
extern void kdb_symbol_print(unsigned long, const kdb_symtab_t *, unsigned int); extern void kdb_symbol_print(unsigned long, const kdb_symtab_t *, unsigned int);
...@@ -165,19 +164,6 @@ typedef struct _kdb_bp { ...@@ -165,19 +164,6 @@ typedef struct _kdb_bp {
#ifdef CONFIG_KGDB_KDB #ifdef CONFIG_KGDB_KDB
extern kdb_bp_t kdb_breakpoints[/* KDB_MAXBPT */]; extern kdb_bp_t kdb_breakpoints[/* KDB_MAXBPT */];
/* The KDB shell command table */
typedef struct _kdbtab {
char *cmd_name; /* Command name */
kdb_func_t cmd_func; /* Function to execute command */
char *cmd_usage; /* Usage String for this command */
char *cmd_help; /* Help message for this command */
short cmd_minlen; /* Minimum legal # command
* chars required */
kdb_cmdflags_t cmd_flags; /* Command behaviour flags */
struct list_head list_node; /* Command list */
bool is_dynamic; /* Command table allocation type */
} kdbtab_t;
extern void kdb_register_table(kdbtab_t *kp, size_t len); extern void kdb_register_table(kdbtab_t *kp, size_t len);
extern int kdb_bt(int, const char **); /* KDB display back trace */ extern int kdb_bt(int, const char **); /* KDB display back trace */
...@@ -233,10 +219,6 @@ extern struct task_struct *kdb_curr_task(int); ...@@ -233,10 +219,6 @@ extern struct task_struct *kdb_curr_task(int);
#define GFP_KDB (in_dbg_master() ? GFP_ATOMIC : GFP_KERNEL) #define GFP_KDB (in_dbg_master() ? GFP_ATOMIC : GFP_KERNEL)
extern void *debug_kmalloc(size_t size, gfp_t flags);
extern void debug_kfree(void *);
extern void debug_kusage(void);
extern struct task_struct *kdb_current_task; extern struct task_struct *kdb_current_task;
extern struct pt_regs *kdb_current_regs; extern struct pt_regs *kdb_current_regs;
......
...@@ -51,48 +51,48 @@ int kdbgetsymval(const char *symname, kdb_symtab_t *symtab) ...@@ -51,48 +51,48 @@ int kdbgetsymval(const char *symname, kdb_symtab_t *symtab)
} }
EXPORT_SYMBOL(kdbgetsymval); EXPORT_SYMBOL(kdbgetsymval);
static char *kdb_name_table[100]; /* arbitrary size */ /**
* kdbnearsym() - Return the name of the symbol with the nearest address
/* * less than @addr.
* kdbnearsym - Return the name of the symbol with the nearest address * @addr: Address to check for near symbol
* less than 'addr'. * @symtab: Structure to receive results
* *
* Parameters: * WARNING: This function may return a pointer to a single statically
* addr Address to check for symbol near * allocated buffer (namebuf). kdb's unusual calling context (single
* symtab Structure to receive results * threaded, all other CPUs halted) provides us sufficient locking for
* Returns: * this to be safe. The only constraint imposed by the static buffer is
* 0 No sections contain this address, symtab zero filled * that the caller must consume any previous reply prior to another call
* 1 Address mapped to module/symbol/section, data in symtab * to lookup a new symbol.
* Remarks: *
* 2.6 kallsyms has a "feature" where it unpacks the name into a * Note that, strictly speaking, some architectures may re-enter the kdb
* string. If that string is reused before the caller expects it * trap if the system turns out to be very badly damaged and this breaks
* then the caller sees its string change without warning. To * the single-threaded assumption above. In these circumstances successful
* avoid cluttering up the main kdb code with lots of kdb_strdup, * continuation and exit from the inner trap is unlikely to work and any
* tests and kfree calls, kdbnearsym maintains an LRU list of the * user attempting this receives a prominent warning before being allowed
* last few unique strings. The list is sized large enough to * to progress. In these circumstances we remain memory safe because
* hold active strings, no kdb caller of kdbnearsym makes more * namebuf[KSYM_NAME_LEN-1] will never change from '\0' although we do
* than ~20 later calls before using a saved value. * tolerate the possibility of garbled symbol display from the outer kdb
* trap.
*
* Return:
* * 0 - No sections contain this address, symtab zero filled
* * 1 - Address mapped to module/symbol/section, data in symtab
*/ */
int kdbnearsym(unsigned long addr, kdb_symtab_t *symtab) int kdbnearsym(unsigned long addr, kdb_symtab_t *symtab)
{ {
int ret = 0; int ret = 0;
unsigned long symbolsize = 0; unsigned long symbolsize = 0;
unsigned long offset = 0; unsigned long offset = 0;
#define knt1_size 128 /* must be >= kallsyms table size */ static char namebuf[KSYM_NAME_LEN];
char *knt1 = NULL;
kdb_dbg_printf(AR, "addr=0x%lx, symtab=%px\n", addr, symtab); kdb_dbg_printf(AR, "addr=0x%lx, symtab=%px\n", addr, symtab);
memset(symtab, 0, sizeof(*symtab)); memset(symtab, 0, sizeof(*symtab));
if (addr < 4096) if (addr < 4096)
goto out; goto out;
knt1 = debug_kmalloc(knt1_size, GFP_ATOMIC);
if (!knt1) {
kdb_func_printf("addr=0x%lx cannot kmalloc knt1\n", addr);
goto out;
}
symtab->sym_name = kallsyms_lookup(addr, &symbolsize , &offset, symtab->sym_name = kallsyms_lookup(addr, &symbolsize , &offset,
(char **)(&symtab->mod_name), knt1); (char **)(&symtab->mod_name), namebuf);
if (offset > 8*1024*1024) { if (offset > 8*1024*1024) {
symtab->sym_name = NULL; symtab->sym_name = NULL;
addr = offset = symbolsize = 0; addr = offset = symbolsize = 0;
...@@ -101,63 +101,14 @@ int kdbnearsym(unsigned long addr, kdb_symtab_t *symtab) ...@@ -101,63 +101,14 @@ int kdbnearsym(unsigned long addr, kdb_symtab_t *symtab)
symtab->sym_end = symtab->sym_start + symbolsize; symtab->sym_end = symtab->sym_start + symbolsize;
ret = symtab->sym_name != NULL && *(symtab->sym_name) != '\0'; ret = symtab->sym_name != NULL && *(symtab->sym_name) != '\0';
if (ret) {
int i;
/* Another 2.6 kallsyms "feature". Sometimes the sym_name is
* set but the buffer passed into kallsyms_lookup is not used,
* so it contains garbage. The caller has to work out which
* buffer needs to be saved.
*
* What was Rusty smoking when he wrote that code?
*/
if (symtab->sym_name != knt1) {
strncpy(knt1, symtab->sym_name, knt1_size);
knt1[knt1_size-1] = '\0';
}
for (i = 0; i < ARRAY_SIZE(kdb_name_table); ++i) {
if (kdb_name_table[i] &&
strcmp(kdb_name_table[i], knt1) == 0)
break;
}
if (i >= ARRAY_SIZE(kdb_name_table)) {
debug_kfree(kdb_name_table[0]);
memmove(kdb_name_table, kdb_name_table+1,
sizeof(kdb_name_table[0]) *
(ARRAY_SIZE(kdb_name_table)-1));
} else {
debug_kfree(knt1);
knt1 = kdb_name_table[i];
memmove(kdb_name_table+i, kdb_name_table+i+1,
sizeof(kdb_name_table[0]) *
(ARRAY_SIZE(kdb_name_table)-i-1));
}
i = ARRAY_SIZE(kdb_name_table) - 1;
kdb_name_table[i] = knt1;
symtab->sym_name = kdb_name_table[i];
knt1 = NULL;
}
if (symtab->mod_name == NULL) if (symtab->mod_name == NULL)
symtab->mod_name = "kernel"; symtab->mod_name = "kernel";
kdb_dbg_printf(AR, "returns %d symtab->sym_start=0x%lx, symtab->mod_name=%px, symtab->sym_name=%px (%s)\n", kdb_dbg_printf(AR, "returns %d symtab->sym_start=0x%lx, symtab->mod_name=%px, symtab->sym_name=%px (%s)\n",
ret, symtab->sym_start, symtab->mod_name, symtab->sym_name, symtab->sym_name); ret, symtab->sym_start, symtab->mod_name, symtab->sym_name, symtab->sym_name);
out: out:
debug_kfree(knt1);
return ret; return ret;
} }
void kdbnearsym_cleanup(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(kdb_name_table); ++i) {
if (kdb_name_table[i]) {
debug_kfree(kdb_name_table[i]);
kdb_name_table[i] = NULL;
}
}
}
static char ks_namebuf[KSYM_NAME_LEN+1], ks_namebuf_prev[KSYM_NAME_LEN+1]; static char ks_namebuf[KSYM_NAME_LEN+1], ks_namebuf_prev[KSYM_NAME_LEN+1];
/* /*
...@@ -655,230 +606,6 @@ unsigned long kdb_task_state(const struct task_struct *p, unsigned long mask) ...@@ -655,230 +606,6 @@ unsigned long kdb_task_state(const struct task_struct *p, unsigned long mask)
return (mask & kdb_task_state_string(state)) != 0; return (mask & kdb_task_state_string(state)) != 0;
} }
/* Last ditch allocator for debugging, so we can still debug even when
* the GFP_ATOMIC pool has been exhausted. The algorithms are tuned
* for space usage, not for speed. One smallish memory pool, the free
* chain is always in ascending address order to allow coalescing,
* allocations are done in brute force best fit.
*/
struct debug_alloc_header {
u32 next; /* offset of next header from start of pool */
u32 size;
void *caller;
};
/* The memory returned by this allocator must be aligned, which means
* so must the header size. Do not assume that sizeof(struct
* debug_alloc_header) is a multiple of the alignment, explicitly
* calculate the overhead of this header, including the alignment.
* The rest of this code must not use sizeof() on any header or
* pointer to a header.
*/
#define dah_align 8
#define dah_overhead ALIGN(sizeof(struct debug_alloc_header), dah_align)
static u64 debug_alloc_pool_aligned[256*1024/dah_align]; /* 256K pool */
static char *debug_alloc_pool = (char *)debug_alloc_pool_aligned;
static u32 dah_first, dah_first_call = 1, dah_used, dah_used_max;
/* Locking is awkward. The debug code is called from all contexts,
* including non maskable interrupts. A normal spinlock is not safe
* in NMI context. Try to get the debug allocator lock, if it cannot
* be obtained after a second then give up. If the lock could not be
* previously obtained on this cpu then only try once.
*
* sparse has no annotation for "this function _sometimes_ acquires a
* lock", so fudge the acquire/release notation.
*/
static DEFINE_SPINLOCK(dap_lock);
static int get_dap_lock(void)
__acquires(dap_lock)
{
static int dap_locked = -1;
int count;
if (dap_locked == smp_processor_id())
count = 1;
else
count = 1000;
while (1) {
if (spin_trylock(&dap_lock)) {
dap_locked = -1;
return 1;
}
if (!count--)
break;
udelay(1000);
}
dap_locked = smp_processor_id();
__acquire(dap_lock);
return 0;
}
void *debug_kmalloc(size_t size, gfp_t flags)
{
unsigned int rem, h_offset;
struct debug_alloc_header *best, *bestprev, *prev, *h;
void *p = NULL;
if (!get_dap_lock()) {
__release(dap_lock); /* we never actually got it */
return NULL;
}
h = (struct debug_alloc_header *)(debug_alloc_pool + dah_first);
if (dah_first_call) {
h->size = sizeof(debug_alloc_pool_aligned) - dah_overhead;
dah_first_call = 0;
}
size = ALIGN(size, dah_align);
prev = best = bestprev = NULL;
while (1) {
if (h->size >= size && (!best || h->size < best->size)) {
best = h;
bestprev = prev;
if (h->size == size)
break;
}
if (!h->next)
break;
prev = h;
h = (struct debug_alloc_header *)(debug_alloc_pool + h->next);
}
if (!best)
goto out;
rem = best->size - size;
/* The pool must always contain at least one header */
if (best->next == 0 && bestprev == NULL && rem < dah_overhead)
goto out;
if (rem >= dah_overhead) {
best->size = size;
h_offset = ((char *)best - debug_alloc_pool) +
dah_overhead + best->size;
h = (struct debug_alloc_header *)(debug_alloc_pool + h_offset);
h->size = rem - dah_overhead;
h->next = best->next;
} else
h_offset = best->next;
best->caller = __builtin_return_address(0);
dah_used += best->size;
dah_used_max = max(dah_used, dah_used_max);
if (bestprev)
bestprev->next = h_offset;
else
dah_first = h_offset;
p = (char *)best + dah_overhead;
memset(p, POISON_INUSE, best->size - 1);
*((char *)p + best->size - 1) = POISON_END;
out:
spin_unlock(&dap_lock);
return p;
}
void debug_kfree(void *p)
{
struct debug_alloc_header *h;
unsigned int h_offset;
if (!p)
return;
if ((char *)p < debug_alloc_pool ||
(char *)p >= debug_alloc_pool + sizeof(debug_alloc_pool_aligned)) {
kfree(p);
return;
}
if (!get_dap_lock()) {
__release(dap_lock); /* we never actually got it */
return; /* memory leak, cannot be helped */
}
h = (struct debug_alloc_header *)((char *)p - dah_overhead);
memset(p, POISON_FREE, h->size - 1);
*((char *)p + h->size - 1) = POISON_END;
h->caller = NULL;
dah_used -= h->size;
h_offset = (char *)h - debug_alloc_pool;
if (h_offset < dah_first) {
h->next = dah_first;
dah_first = h_offset;
} else {
struct debug_alloc_header *prev;
unsigned int prev_offset;
prev = (struct debug_alloc_header *)(debug_alloc_pool +
dah_first);
while (1) {
if (!prev->next || prev->next > h_offset)
break;
prev = (struct debug_alloc_header *)
(debug_alloc_pool + prev->next);
}
prev_offset = (char *)prev - debug_alloc_pool;
if (prev_offset + dah_overhead + prev->size == h_offset) {
prev->size += dah_overhead + h->size;
memset(h, POISON_FREE, dah_overhead - 1);
*((char *)h + dah_overhead - 1) = POISON_END;
h = prev;
h_offset = prev_offset;
} else {
h->next = prev->next;
prev->next = h_offset;
}
}
if (h_offset + dah_overhead + h->size == h->next) {
struct debug_alloc_header *next;
next = (struct debug_alloc_header *)
(debug_alloc_pool + h->next);
h->size += dah_overhead + next->size;
h->next = next->next;
memset(next, POISON_FREE, dah_overhead - 1);
*((char *)next + dah_overhead - 1) = POISON_END;
}
spin_unlock(&dap_lock);
}
void debug_kusage(void)
{
struct debug_alloc_header *h_free, *h_used;
#ifdef CONFIG_IA64
/* FIXME: using dah for ia64 unwind always results in a memory leak.
* Fix that memory leak first, then set debug_kusage_one_time = 1 for
* all architectures.
*/
static int debug_kusage_one_time;
#else
static int debug_kusage_one_time = 1;
#endif
if (!get_dap_lock()) {
__release(dap_lock); /* we never actually got it */
return;
}
h_free = (struct debug_alloc_header *)(debug_alloc_pool + dah_first);
if (dah_first == 0 &&
(h_free->size == sizeof(debug_alloc_pool_aligned) - dah_overhead ||
dah_first_call))
goto out;
if (!debug_kusage_one_time)
goto out;
debug_kusage_one_time = 0;
kdb_func_printf("debug_kmalloc memory leak dah_first %d\n", dah_first);
if (dah_first) {
h_used = (struct debug_alloc_header *)debug_alloc_pool;
kdb_func_printf("h_used %px size %d\n", h_used, h_used->size);
}
do {
h_used = (struct debug_alloc_header *)
((char *)h_free + dah_overhead + h_free->size);
kdb_func_printf("h_used %px size %d caller %px\n",
h_used, h_used->size, h_used->caller);
h_free = (struct debug_alloc_header *)
(debug_alloc_pool + h_free->next);
} while (h_free->next);
h_used = (struct debug_alloc_header *)
((char *)h_free + dah_overhead + h_free->size);
if ((char *)h_used - debug_alloc_pool !=
sizeof(debug_alloc_pool_aligned))
kdb_func_printf("h_used %px size %d caller %px\n",
h_used, h_used->size, h_used->caller);
out:
spin_unlock(&dap_lock);
}
/* Maintain a small stack of kdb_flags to allow recursion without disturbing /* Maintain a small stack of kdb_flags to allow recursion without disturbing
* the global kdb state. * the global kdb state.
*/ */
......
...@@ -147,11 +147,17 @@ static int kdb_ftdump(int argc, const char **argv) ...@@ -147,11 +147,17 @@ static int kdb_ftdump(int argc, const char **argv)
return 0; return 0;
} }
static kdbtab_t ftdump_cmd = {
.name = "ftdump",
.func = kdb_ftdump,
.usage = "[skip_#entries] [cpu]",
.help = "Dump ftrace log; -skip dumps last #entries",
.flags = KDB_ENABLE_ALWAYS_SAFE,
};
static __init int kdb_ftrace_register(void) static __init int kdb_ftrace_register(void)
{ {
kdb_register_flags("ftdump", kdb_ftdump, "[skip_#entries] [cpu]", kdb_register(&ftdump_cmd);
"Dump ftrace log; -skip dumps last #entries", 0,
KDB_ENABLE_ALWAYS_SAFE);
return 0; return 0;
} }
......
...@@ -28,28 +28,26 @@ static int kdb_hello_cmd(int argc, const char **argv) ...@@ -28,28 +28,26 @@ static int kdb_hello_cmd(int argc, const char **argv)
return 0; return 0;
} }
static kdbtab_t hello_cmd = {
.name = "hello",
.func = kdb_hello_cmd,
.usage = "[string]",
.help = "Say Hello World or Hello [string]",
};
static int __init kdb_hello_cmd_init(void) static int __init kdb_hello_cmd_init(void)
{ {
/* /*
* Registration of a dynamically added kdb command is done with * Registration of a dynamically added kdb command is done with
* kdb_register() with the arguments being: * kdb_register().
* 1: The name of the shell command
* 2: The function that processes the command
* 3: Description of the usage of any arguments
* 4: Descriptive text when you run help
* 5: Number of characters to complete the command
* 0 == type the whole command
* 1 == match both "g" and "go" for example
*/ */
kdb_register("hello", kdb_hello_cmd, "[string]", kdb_register(&hello_cmd);
"Say Hello World or Hello [string]", 0);
return 0; return 0;
} }
static void __exit kdb_hello_cmd_exit(void) static void __exit kdb_hello_cmd_exit(void)
{ {
kdb_unregister("hello"); kdb_unregister(&hello_cmd);
} }
module_init(kdb_hello_cmd_init); module_init(kdb_hello_cmd_init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment