aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorJason Wessel <jason.wessel@windriver.com>2012-03-29 18:41:24 -0400
committerJason Wessel <jason.wessel@windriver.com>2012-03-29 18:41:24 -0400
commit486c5987a00a89d56c2c04c506417ef8f823ca2e (patch)
tree9b1ec804014c2f05198661c4f58006acb321ea7f /drivers/misc
parent456ca7ff24841bf2d2a2dfd690fe7d42ef70d932 (diff)
kgdbts: (1 of 2) fix single step awareness to work correctly with SMP
The do_fork and sys_open tests have never worked properly on anything other than a UP configuration with the kgdb test suite. This is because the test suite did not fully implement the behavior of a real debugger. A real debugger tracks the state of what thread it asked to single step and can correctly continue other threads of execution or conditionally stop while waiting for the original thread single step request to return. Below is a simple method to cause a fatal kernel oops with the kgdb test suite on a 4 processor x86 system: while [ 1 ] ; do ls > /dev/null 2> /dev/null; done& while [ 1 ] ; do ls > /dev/null 2> /dev/null; done& while [ 1 ] ; do ls > /dev/null 2> /dev/null; done& while [ 1 ] ; do ls > /dev/null 2> /dev/null; done& echo V1I1F1000 > /sys/module/kgdbts/parameters/kgdbts Very soon after starting the test the kernel will oops with a message like: kgdbts: BP mismatch 3b7da66480 expected ffffffff8106a590 WARNING: at drivers/misc/kgdbts.c:303 check_and_rewind_pc+0xe0/0x100() Call Trace: [<ffffffff812994a0>] check_and_rewind_pc+0xe0/0x100 [<ffffffff81298945>] validate_simple_test+0x25/0xc0 [<ffffffff81298f77>] run_simple_test+0x107/0x2c0 [<ffffffff81298a18>] kgdbts_put_char+0x18/0x20 The warn will turn to a hard kernel crash shortly after that because the pc will not get properly rewound to the right value after hitting a breakpoint leading to a hard lockup. This change is broken up into 2 pieces because archs that have hw single stepping (2.6.26 and up) need different changes than archs that do not have hw single stepping (3.0 and up). This change implements the correct behavior for an arch that supports hw single stepping. A minor defect was fixed where sys_open should be do_sys_open for the sys_open break point test. This solves the problem of running a 64 bit with a 32 bit user space. The sys_open() never gets called when using the 32 bit file system for the kgdb testsuite because the 32 bit binaries invoke the compat_sys_open() call leading to the test never completing. In order to mimic a real debugger, the kgdb test suite now tracks the most recent thread that was continued (cont_thread_id), with the intent to single step just this thread. When the response to the single step request stops in a different thread that hit the original break point that thread will now get continued, while the debugger waits for the thread with the single step pending. Here is a high level description of the sequence of events. cont_instead_of_sstep = 0; 1) set breakpoint at do_fork 2) continue 3) Save the thread id where we stop to cont_thread_id 4) Remove breakpoint at do_fork 5) Reset the PC if needed depending on kernel exception type 6) if (cont_instead_of_sstep) { continue } else { single step } 7) Check where we stopped if current thread != cont_thread_id { cont_instead_of_sstep = 1; goto step 5 } else { cont_instead_of_sstep = 0; } 8) clean up and run test again if needed Cc: stable@vger.kernel.org # >= 2.6.26 Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/kgdbts.c54
1 files changed, 43 insertions, 11 deletions
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
index 997e94d618b..3cad9fce805 100644
--- a/drivers/misc/kgdbts.c
+++ b/drivers/misc/kgdbts.c
@@ -134,6 +134,9 @@ static int force_hwbrks;
134static int hwbreaks_ok; 134static int hwbreaks_ok;
135static int hw_break_val; 135static int hw_break_val;
136static int hw_break_val2; 136static int hw_break_val2;
137static int cont_instead_of_sstep;
138static unsigned long cont_thread_id;
139static unsigned long sstep_thread_id;
137#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC) 140#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC)
138static int arch_needs_sstep_emulation = 1; 141static int arch_needs_sstep_emulation = 1;
139#else 142#else
@@ -211,7 +214,7 @@ static unsigned long lookup_addr(char *arg)
211 if (!strcmp(arg, "kgdbts_break_test")) 214 if (!strcmp(arg, "kgdbts_break_test"))
212 addr = (unsigned long)kgdbts_break_test; 215 addr = (unsigned long)kgdbts_break_test;
213 else if (!strcmp(arg, "sys_open")) 216 else if (!strcmp(arg, "sys_open"))
214 addr = (unsigned long)sys_open; 217 addr = (unsigned long)do_sys_open;
215 else if (!strcmp(arg, "do_fork")) 218 else if (!strcmp(arg, "do_fork"))
216 addr = (unsigned long)do_fork; 219 addr = (unsigned long)do_fork;
217 else if (!strcmp(arg, "hw_break_val")) 220 else if (!strcmp(arg, "hw_break_val"))
@@ -283,6 +286,16 @@ static void hw_break_val_write(void)
283 hw_break_val++; 286 hw_break_val++;
284} 287}
285 288
289static int get_thread_id_continue(char *put_str, char *arg)
290{
291 char *ptr = &put_str[11];
292
293 if (put_str[1] != 'T' || put_str[2] != '0')
294 return 1;
295 kgdb_hex2long(&ptr, &cont_thread_id);
296 return 0;
297}
298
286static int check_and_rewind_pc(char *put_str, char *arg) 299static int check_and_rewind_pc(char *put_str, char *arg)
287{ 300{
288 unsigned long addr = lookup_addr(arg); 301 unsigned long addr = lookup_addr(arg);
@@ -324,6 +337,18 @@ static int check_single_step(char *put_str, char *arg)
324 gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); 337 gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs);
325 v2printk("Singlestep stopped at IP: %lx\n", 338 v2printk("Singlestep stopped at IP: %lx\n",
326 instruction_pointer(&kgdbts_regs)); 339 instruction_pointer(&kgdbts_regs));
340
341 if (sstep_thread_id != cont_thread_id && !arch_needs_sstep_emulation) {
342 /*
343 * Ensure we stopped in the same thread id as before, else the
344 * debugger should continue until the original thread that was
345 * single stepped is scheduled again, emulating gdb's behavior.
346 */
347 v2printk("ThrID does not match: %lx\n", cont_thread_id);
348 cont_instead_of_sstep = 1;
349 ts.idx -= 4;
350 return 0;
351 }
327 if (instruction_pointer(&kgdbts_regs) == addr) { 352 if (instruction_pointer(&kgdbts_regs) == addr) {
328 eprintk("kgdbts: SingleStep failed at %lx\n", 353 eprintk("kgdbts: SingleStep failed at %lx\n",
329 instruction_pointer(&kgdbts_regs)); 354 instruction_pointer(&kgdbts_regs));
@@ -368,7 +393,12 @@ static int got_break(char *put_str, char *arg)
368static void emul_sstep_get(char *arg) 393static void emul_sstep_get(char *arg)
369{ 394{
370 if (!arch_needs_sstep_emulation) { 395 if (!arch_needs_sstep_emulation) {
371 fill_get_buf(arg); 396 if (cont_instead_of_sstep) {
397 cont_instead_of_sstep = 0;
398 fill_get_buf("c");
399 } else {
400 fill_get_buf(arg);
401 }
372 return; 402 return;
373 } 403 }
374 switch (sstep_state) { 404 switch (sstep_state) {
@@ -398,9 +428,11 @@ static void emul_sstep_get(char *arg)
398static int emul_sstep_put(char *put_str, char *arg) 428static int emul_sstep_put(char *put_str, char *arg)
399{ 429{
400 if (!arch_needs_sstep_emulation) { 430 if (!arch_needs_sstep_emulation) {
401 if (!strncmp(put_str+1, arg, 2)) 431 char *ptr = &put_str[11];
402 return 0; 432 if (put_str[1] != 'T' || put_str[2] != '0')
403 return 1; 433 return 1;
434 kgdb_hex2long(&ptr, &sstep_thread_id);
435 return 0;
404 } 436 }
405 switch (sstep_state) { 437 switch (sstep_state) {
406 case 1: 438 case 1:
@@ -502,10 +534,10 @@ static struct test_struct bad_read_test[] = {
502static struct test_struct singlestep_break_test[] = { 534static struct test_struct singlestep_break_test[] = {
503 { "?", "S0*" }, /* Clear break points */ 535 { "?", "S0*" }, /* Clear break points */
504 { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ 536 { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
505 { "c", "T0*", }, /* Continue */ 537 { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */
538 { "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */
506 { "g", "kgdbts_break_test", NULL, check_and_rewind_pc }, 539 { "g", "kgdbts_break_test", NULL, check_and_rewind_pc },
507 { "write", "OK", write_regs }, /* Write registers */ 540 { "write", "OK", write_regs }, /* Write registers */
508 { "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */
509 { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ 541 { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
510 { "g", "kgdbts_break_test", NULL, check_single_step }, 542 { "g", "kgdbts_break_test", NULL, check_single_step },
511 { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ 543 { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
@@ -523,10 +555,10 @@ static struct test_struct singlestep_break_test[] = {
523static struct test_struct do_fork_test[] = { 555static struct test_struct do_fork_test[] = {
524 { "?", "S0*" }, /* Clear break points */ 556 { "?", "S0*" }, /* Clear break points */
525 { "do_fork", "OK", sw_break, }, /* set sw breakpoint */ 557 { "do_fork", "OK", sw_break, }, /* set sw breakpoint */
526 { "c", "T0*", }, /* Continue */ 558 { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */
559 { "do_fork", "OK", sw_rem_break }, /*remove breakpoint */
527 { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */ 560 { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */
528 { "write", "OK", write_regs }, /* Write registers */ 561 { "write", "OK", write_regs }, /* Write registers */
529 { "do_fork", "OK", sw_rem_break }, /*remove breakpoint */
530 { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ 562 { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
531 { "g", "do_fork", NULL, check_single_step }, 563 { "g", "do_fork", NULL, check_single_step },
532 { "do_fork", "OK", sw_break, }, /* set sw breakpoint */ 564 { "do_fork", "OK", sw_break, }, /* set sw breakpoint */
@@ -541,10 +573,10 @@ static struct test_struct do_fork_test[] = {
541static struct test_struct sys_open_test[] = { 573static struct test_struct sys_open_test[] = {
542 { "?", "S0*" }, /* Clear break points */ 574 { "?", "S0*" }, /* Clear break points */
543 { "sys_open", "OK", sw_break, }, /* set sw breakpoint */ 575 { "sys_open", "OK", sw_break, }, /* set sw breakpoint */
544 { "c", "T0*", }, /* Continue */ 576 { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */
577 { "sys_open", "OK", sw_rem_break }, /*remove breakpoint */
545 { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */ 578 { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */
546 { "write", "OK", write_regs }, /* Write registers */ 579 { "write", "OK", write_regs }, /* Write registers */
547 { "sys_open", "OK", sw_rem_break }, /*remove breakpoint */
548 { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ 580 { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
549 { "g", "sys_open", NULL, check_single_step }, 581 { "g", "sys_open", NULL, check_single_step },
550 { "sys_open", "OK", sw_break, }, /* set sw breakpoint */ 582 { "sys_open", "OK", sw_break, }, /* set sw breakpoint */