--- arch/x86/kernel/traps_32.c | 1 arch/x86/kernel/traps_64.c | 2 arch/x86/mm/fault_32.c | 1 arch/x86/mm/fault_64.c | 1 drivers/char/sysrq.c | 22 ++++++++ include/linux/logdev.h | 27 ++++++++++ kernel/logdev/logdev.c | 113 +++++++++++++++++++++++++++++++++++++++++++++ kernel/sched.c | 3 + lib/Kconfig.debug | 8 +++ 9 files changed, 177 insertions(+), 1 deletion(-) Index: linux-2.6.24-rc1/kernel/sched.c =================================================================== --- linux-2.6.24-rc1.orig/kernel/sched.c 2007-11-01 18:44:27.000000000 -0400 +++ linux-2.6.24-rc1/kernel/sched.c 2007-11-01 19:18:29.000000000 -0400 @@ -64,6 +64,7 @@ #include #include #include +#include #include @@ -1929,6 +1930,8 @@ context_switch(struct rq *rq, struct tas spin_release(&rq->lock.dep_map, 1, _THIS_IP_); #endif + logdev_record_switch(prev, next); + /* Here we just switch the register state and the stack. */ switch_to(prev, next, prev); Index: linux-2.6.24-rc1/arch/x86/kernel/traps_64.c =================================================================== --- linux-2.6.24-rc1.orig/arch/x86/kernel/traps_64.c 2007-11-01 19:07:50.000000000 -0400 +++ linux-2.6.24-rc1/arch/x86/kernel/traps_64.c 2007-11-01 19:18:09.000000000 -0400 @@ -526,6 +526,7 @@ void __kprobes __die(const char * str, s printk_address(regs->rip); printk(" RSP <%016lx>\n", regs->rsp); logdev_print_off(); + logdev_switch_off(); logdev_dump(); if (kexec_should_crash(current)) crash_kexec(regs); @@ -554,6 +555,7 @@ void __kprobes die_nmi(char *str, struct printk(str, smp_processor_id()); show_registers(regs); logdev_print_off(); + logdev_switch_off(); logdev_dump(); if (kexec_should_crash(current)) crash_kexec(regs); Index: linux-2.6.24-rc1/arch/x86/mm/fault_64.c =================================================================== --- linux-2.6.24-rc1.orig/arch/x86/mm/fault_64.c 2007-11-01 19:10:15.000000000 -0400 +++ linux-2.6.24-rc1/arch/x86/mm/fault_64.c 2007-11-01 19:18:09.000000000 -0400 @@ -542,6 +542,7 @@ no_context: /* Executive summary in case the body of the oops scrolled away */ printk(KERN_EMERG "CR2: %016lx\n", address); logdev_print_off(); + logdev_switch_off(); logdev_dump(); oops_end(flags); do_exit(SIGKILL); Index: linux-2.6.24-rc1/drivers/char/sysrq.c =================================================================== --- linux-2.6.24-rc1.orig/drivers/char/sysrq.c 2007-11-01 19:07:50.000000000 -0400 +++ linux-2.6.24-rc1/drivers/char/sysrq.c 2007-11-01 19:18:09.000000000 -0400 @@ -194,6 +194,26 @@ static struct sysrq_key_op sysrq_dumplog .enable_mask = SYSRQ_ENABLE_DUMP, }; +static void sysrq_handle_togglelogswitch(int key, struct tty_struct *tty) +{ + /* + * Not so atomic, but we really don't care! + */ + if (logdev_switch_ison()) { + logdev_switch_off(); + printk("logdev_switch now off\n"); + } else { + logdev_switch_on(); + printk("logdev_switch now on\n"); + } +} + +static struct sysrq_key_op sysrq_togglelogdevswitch_op = { + .handler = sysrq_handle_togglelogswitch, + .help_msg = "Togglelogswitch", + .action_msg = "Toggling logdev_switch", +}; + /* END SYNC SYSRQ HANDLERS BLOCK */ #ifdef CONFIG_LOCKDEP @@ -350,7 +370,7 @@ static struct sysrq_key_op *sysrq_key_ta &sysrq_showlocks_op, /* d */ &sysrq_term_op, /* e */ /* g: May be registered by ppc for kgdb */ - NULL, /* f */ + &sysrq_togglelogdevswitch_op, /* f */ &sysrq_dumplog_op, /* g */ NULL, /* h */ &sysrq_kill_op, /* i */ Index: linux-2.6.24-rc1/include/linux/logdev.h =================================================================== --- linux-2.6.24-rc1.orig/include/linux/logdev.h 2007-11-01 19:07:50.000000000 -0400 +++ linux-2.6.24-rc1/include/linux/logdev.h 2007-11-01 19:18:09.000000000 -0400 @@ -12,6 +12,21 @@ #include +struct logdev_switch_struct { + unsigned long long t; + short pid_prev; + short pid_next; + int prev_prio; + int prev_static_prio; + int prev_normal_prio; + int prev_state; + int next_prio; + int next_static_prio; + int next_normal_prio; + char prev_comm[TASK_COMM_LEN]; + char next_comm[TASK_COMM_LEN]; +} __attribute__((packed)); + struct logdev_print { char str[0]; }; @@ -66,6 +81,7 @@ struct logdev_header { struct logdev_item { struct logdev_header hdr; union { + struct logdev_switch_struct sw; struct logdev_print print; struct logdev_print_time print_time; struct logdev_print_time_func print_time_func; @@ -80,6 +96,7 @@ extern int in_logdump; extern unsigned long logdev_switches; #define LOGDEV_SW_PRINT_ENABLED 0 +#define LOGDEV_SW_SWITCH_ENABLED 1 #define LOGDEV_SW_MARKER_ENABLED 2 #define LOGDEV_SW_BITS 3 @@ -91,6 +108,10 @@ extern unsigned long logdev_switches; #define logdev_print_on() LOGDEV_SW_SET(LOGDEV_SW_PRINT_ENABLED); #define logdev_print_off() LOGDEV_SW_CLEAR(LOGDEV_SW_PRINT_ENABLED); +#define logdev_switch_ison() LOGDEV_SW_ISSET(LOGDEV_SW_SWITCH_ENABLED) +#define logdev_switch_on() LOGDEV_SW_SET(LOGDEV_SW_SWITCH_ENABLED); +#define logdev_switch_off() LOGDEV_SW_CLEAR(LOGDEV_SW_SWITCH_ENABLED); + #define logdev_mark_ison() LOGDEV_SW_ISSET(LOGDEV_SW_MARKER_ENABLED) #define logdev_mark_on() LOGDEV_SW_SET(LOGDEV_SW_MARKER_ENABLED); #define logdev_mark_off() LOGDEV_SW_CLEAR(LOGDEV_SW_MARKER_ENABLED); @@ -127,6 +148,7 @@ int __logdev_print_time_func(int curr, c */ int logdev_record(int id, int total_size, const void *data, int size, ...); void logdev_dump(void); +void logdev_record_switch(struct task_struct *prev, struct task_struct *next); #define LOGDEV(x,y...) logdev_##x(y) #define LOGPRINTS(func,x...) do { if (logdev_print_ison()) LOGDEV(func,x); } while(0) @@ -175,6 +197,7 @@ int logdev_init(void); /* If we want to #else /* !LOGDEV */ #define logdev_dump() do {} while(0) #define logdev_record(id, size, data) do {} while(0) +#define logdev_record_switch(prev, next) do {} while(0) #define LOGPRINT(x...) do {} while(0) #define LOGTPRINT(x...) do {} while(0) @@ -187,6 +210,10 @@ int logdev_init(void); /* If we want to #define logdev_print_on() do {} while(0) #define logdev_print_off() do {} while(0) +#define logdev_switch_ison() ( 0 ) +#define logdev_switch_on() do {} while(0) +#define logdev_switch_off() do {} while(0) + #define logdev_print(x...) do {} while(0) #define logdev_mark() do {} while(0) Index: linux-2.6.24-rc1/kernel/logdev/logdev.c =================================================================== --- linux-2.6.24-rc1.orig/kernel/logdev/logdev.c 2007-11-01 19:07:50.000000000 -0400 +++ linux-2.6.24-rc1/kernel/logdev/logdev.c 2007-11-01 19:18:09.000000000 -0400 @@ -99,7 +99,16 @@ static int option_logdev_print(char *opt return 0; } +static int option_logdev_switch(char *opt) +{ + logdev_switch_on(); + if (strstr(opt, "0")) + logdev_switch_on(); + return 0; +} + __setup("logdevprint", option_logdev_print); +__setup("logdevswitch", option_logdev_switch); /* * The following is to register call back functions to print out @@ -177,6 +186,60 @@ int __kprobes logdev_unregister_callback static char kern_buffer[LOGDEV_CPUS][PAGE_SIZE]; /** + * logdev_record_switch is used to track context switches. + * @prev - previous task + * @next - next task + * + * If the logdev_switch is not set, then this doesn't record. Thus allowing + * you to just record the context switches that are needed to record. + * Just use logdev_switch_on and logdev_switch_off to turn on this function. + * + * This is called from schedule, and interrupts should already be turned off. + */ +void __kprobes logdev_record_switch(struct task_struct *prev, + struct task_struct *next) +{ + struct logdev_dev *dev; + struct logdev_header hdr; + struct logdev_switch_struct rs; + int cpu = smp_processor_id(); + + if (!logdev_switch_ison()) + return; + + if (check_cpu(cpu)) + return; + + dev = get_logdev(cpu); + if (!dev_running(dev)) + return; + + WARN_ON(!irqs_disabled()); + + rs.t = sched_clock(); + rs.pid_prev = prev->pid; + rs.prev_prio = prev->prio; + rs.prev_static_prio = prev->static_prio; + rs.prev_normal_prio = prev->normal_prio; + rs.prev_state = prev->state; + rs.pid_next = next->pid; + rs.next_prio = next->prio; + rs.next_static_prio = next->static_prio; + rs.next_normal_prio = next->normal_prio; + memcpy(rs.prev_comm, prev->comm, TASK_COMM_LEN); + memcpy(rs.next_comm, next->comm, TASK_COMM_LEN); + + hdr.counter = logdev_counter_inc(); + hdr.id = LOGDEV_SWITCH_ID; + hdr.size = sizeof(hdr) + sizeof(rs); + + spin_lock(&dev->lock); + logdev_copy_to_dev(dev,&hdr,sizeof(hdr)); + logdev_copy_to_dev(dev,&rs,sizeof(rs)); + spin_unlock(&dev->lock); +} + +/** * logdev_vprint - print to the log like vprintk. * @str - format string * @va - variable list argument. @@ -779,6 +842,42 @@ static int __kprobes process_log(struct } break; + case LOGDEV_SWITCH_ID: + { + static const char stat_nam[] = "RSDTtZX"; + struct logdev_switch_struct rs; + unsigned state; + char c; + + printk(">>>> IN LOGDEV SWITCH <<<< cpu:%d \n", cpu); + + logdev_copy_from_dev(dev,&rs,sizeof(rs)); + + printk("CPU=%d ", cpu); + logdev_time(rs.t); + + state = rs.prev_state ? __ffs(rs.prev_state) + 1 : 0; + + c = state < sizeof(stat_nam) - 1 ? stat_nam[state] : '?'; + + printk("%s:%d(%d:%d:%d:%c", + rs.prev_comm, + rs.pid_prev, + rs.prev_prio, + rs.prev_static_prio, + rs.prev_normal_prio, + c); + + printk(") -->> %s:%d(%d:%d:%d)\n", + rs.next_comm, + rs.pid_next, + rs.next_prio, + rs.next_static_prio, + rs.next_normal_prio); + + break; + } + case LOGDEV_CUSTOM: { struct list_head *p; @@ -884,6 +983,7 @@ static int __kprobes flush_buffer(struct */ void __kprobes logdev_dump(void) { + int save_switch; int save_print; struct logdev_dev *dev; struct logdev_header hdr[LOGDEV_CPUS]; @@ -918,7 +1018,9 @@ void __kprobes logdev_dump(void) /* * This is for debugging, so we don't want to reintroduce more output. */ + save_switch = logdev_switch_ison(); save_print = logdev_print_ison(); + logdev_switch_off(); logdev_print_off(); printk("****** Starting Logdev Dump ********\n"); @@ -1007,6 +1109,8 @@ void __kprobes logdev_dump(void) } if (save_print) logdev_print_on(); + if (save_switch) + logdev_switch_on(); local_irq_restore(flags); started = 0; @@ -1018,6 +1122,7 @@ static int __kprobes logdev_panic_handle void *unused) { logdev_print_off(); + logdev_switch_off(); logdev_dump(); return NOTIFY_OK; } @@ -1127,6 +1232,7 @@ EXPORT_SYMBOL_GPL(logdev_d); static int logdev_debugfs_init(void) { struct dentry *d_switches; + struct dentry *d_switch; struct dentry *d_print; #ifndef CONFIG_LOGDEV_MARKER struct dentry *d_mark; @@ -1147,6 +1253,9 @@ static int logdev_debugfs_init(void) d_switches = debugfs_create_file("switches", 0444, logdev_d, &logdev_switches, &logdev_debug_rdonly_fops); + d_switch = debugfs_create_file("switch", 0644, logdev_d, + (void*)LOGDEV_SW_SWITCH_ENABLED, + &logdev_debug_sw_fops); d_print = debugfs_create_file("print", 0644, logdev_d, (void*)LOGDEV_SW_PRINT_ENABLED, &logdev_debug_sw_fops); @@ -1221,6 +1330,9 @@ int __init logdev_init(void) #ifdef CONFIG_LOGDEV_PRINT_ENABLED logdev_print_on(); #endif +#ifdef CONFIG_LOGDEV_SWITCH_ENABLED + logdev_switch_on(); +#endif out: return res; @@ -1230,6 +1342,7 @@ fail: goto out; } +EXPORT_SYMBOL_GPL(logdev_record_switch); EXPORT_SYMBOL_GPL(logdev_time); EXPORT_SYMBOL_GPL(logdev_print); EXPORT_SYMBOL_GPL(logdev_print_time); Index: linux-2.6.24-rc1/lib/Kconfig.debug =================================================================== --- linux-2.6.24-rc1.orig/lib/Kconfig.debug 2007-11-01 19:08:53.000000000 -0400 +++ linux-2.6.24-rc1/lib/Kconfig.debug 2007-11-01 19:18:09.000000000 -0400 @@ -503,6 +503,14 @@ config LOGDEV_PRINT_ENABLED as soon as the logdev device is loaded. Otherwise you must enable it with /proc/logdev/print +config LOGDEV_SWITCH_ENABLED + bool "Default Logdev printing of context switches on startup" + depends on LOGDEV + help + Enable this if you expect the LOGSWITCH macros to be enabled + as soon as the logdev device is loaded. Otherwise you must + enable it with /proc/logdev/switch + config RCU_TORTURE_TEST tristate "torture tests for RCU" depends on DEBUG_KERNEL Index: linux-2.6.24-rc1/arch/x86/kernel/traps_32.c =================================================================== --- linux-2.6.24-rc1.orig/arch/x86/kernel/traps_32.c 2007-11-01 19:07:50.000000000 -0400 +++ linux-2.6.24-rc1/arch/x86/kernel/traps_32.c 2007-11-01 19:18:09.000000000 -0400 @@ -696,6 +696,7 @@ void __kprobes die_nmi(struct pt_regs *r spin_lock(&nmi_print_lock); logdev_print_off(); + logdev_switch_off(); /* * We are in trouble anyway, lets at least try * to get a message out. Index: linux-2.6.24-rc1/arch/x86/mm/fault_32.c =================================================================== --- linux-2.6.24-rc1.orig/arch/x86/mm/fault_32.c 2007-11-01 19:09:52.000000000 -0400 +++ linux-2.6.24-rc1/arch/x86/mm/fault_32.c 2007-11-01 19:18:09.000000000 -0400 @@ -525,6 +525,7 @@ no_context: lfnprint("BUG!"); logdev_print_off(); + logdev_switch_off(); #ifdef CONFIG_X86_PAE if (error_code & 16) {